]> git.pld-linux.org Git - packages/kernel.git/commitdiff
- netfilter snapshot 20030924
authorcieciwa <cieciwa@pld-linux.org>
Wed, 24 Sep 2003 11:43:50 +0000 (11:43 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Changed files:
    linux-2.4.20-netfilter-1.2.8_20030924.patch -> 1.1

linux-2.4.20-netfilter-1.2.8_20030924.patch [new file with mode: 0644]

diff --git a/linux-2.4.20-netfilter-1.2.8_20030924.patch b/linux-2.4.20-netfilter-1.2.8_20030924.patch
new file mode 100644 (file)
index 0000000..f50f9c7
--- /dev/null
@@ -0,0 +1,29498 @@
+diff -Nur --exclude '*.orig' linux-2.4.20.org/Documentation/Configure.help linux-2.4.20/Documentation/Configure.help
+--- linux-2.4.20.org/Documentation/Configure.help      Wed Sep 24 08:52:58 2003
++++ linux-2.4.20/Documentation/Configure.help  Wed Sep 24 09:18:15 2003
+@@ -2543,6 +2543,50 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++Amanda protocol support
++CONFIG_IP_NF_AMANDA
++  If you are running the Amanda backup package (http://www.amanda.org/)
++  on this machine or machines that will be MASQUERADED through this
++  machine, then you may want to enable this feature.  This allows the
++  connection tracking and natting code to allow the sub-channels that
++  Amanda requires for communication of the backup data, messages and
++  index.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++CuSeeMe protocol support
++CONFIG_IP_NF_CUSEEME
++  The CuSeeMe conferencing protocol is problematic when used in
++  conjunction with NAT; even though there are no random ports used for
++  extra connections, the messages contain IP addresses inside them.
++  This NAT helper mangles the IP address inside packets so both
++  parties don't get confused.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `Y'.
++
++MMS protocol support
++CONFIG_IP_NF_MMS
++  Tracking MMS (Microsoft Windows Media Services) connections
++  could be problematic if random ports are used to send the
++  streaming content. This option allows users to track streaming
++  connections over random UDP or TCP ports.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `Y'.
++
++Quake III Arena protocol support
++CONFIG_IP_NF_QUAKE3
++  Quake III Arena  connection tracking helper. This module allows for a
++  stricter firewall rulebase if one only allows traffic to a master
++  server. Connections to Quake III server IP addresses and ports returned
++  by the master server will be tracked automatically.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `Y'.
++
+ IRC Send/Chat protocol support
+ CONFIG_IP_NF_IRC
+   There is a commonly-used extension to IRC called
+@@ -2557,6 +2601,118 @@
+   If you want to compile it as a module, say 'M' here and read
+   Documentation/modules.txt.  If unsure, say 'N'.
++TFTP protocol support
++CONFIG_IP_NF_TFTP
++  TFTP connection tracking helper, this is required depending
++  on how restrictive your ruleset is.
++  If you are using a tftp client behind -j SNAT or -j MASQUERADING
++  you will need this.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `Y'.
++
++Per connection mark support
++CONFIG_IP_NF_CONNTRACK_MARK
++  This option enables support for connection marks, used by the
++  `CONNMARK' target and `connmark' match. Similar to the mark value
++  of packets, but this mark value is kept in the conntrack session
++  instead of the individual packets.
++
++CONNMARK target support
++CONFIG_IP_NF_TARGET_CONNMARK
++  This option adds a `CONNMARK' target, which allows one to manipulate
++  the connection mark value.  Similar to the MARK target, but
++  affects the connection mark value rather than the packet mark value.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  The module will be called
++  ipt_CONNMARK.o.  If unsure, say `N'.
++
++connmark match support
++CONFIP_IP_NF_MATCH_CONNMARK
++  This option adds a `connmark' match, which allows you to match the
++  connection mark value previously set for the session by `CONNMARK'. 
++
++Eggdrop bot support
++CONFIG_IP_NF_EGG
++  If you are running an eggdrop hub bot on this machine, then you
++  may want to enable this feature. This enables eggdrop bots to share
++  their user file to other eggdrop bots.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++H.323 (netmeeting) support
++CONFIG_IP_NF_H323
++  H.323 is a standard signalling protocol used by teleconferencing
++  softwares like netmeeting. With the ip_conntrack_h323 and
++  the ip_nat_h323 modules you can support the protocol on a connection
++  tracking/NATing firewall.
++
++  If you want to compile it as a module, say 'M' here and read
++  Documentation/modules.txt.  If unsure, say 'N'.
++
++PPTP conntrack and NAT support
++CONFIG_IP_NF_PPTP
++  This module adds support for PPTP (Point to Point Tunnelling Protocol, 
++  RFC2637) conncection tracking and NAT. 
++
++  If you are running PPTP sessions over a stateful firewall or NAT box,
++  you may want to enable this feature.  
++
++  Please note that not all PPTP modes of operation are supported yet.
++  For more info, read top of the file net/ipv4/netfilter/ip_conntrack_pptp.c
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++GRE protocol conntrack and NAT support
++CONFIG_IP_NF_CT_PROTO_GRE
++  This module adds generic support for connection tracking and NAT of the
++  GRE protocol (RFC1701, RFC2784).  Please note that this will only work
++  with GRE connections using the key field of the GRE header.
++
++  You will need GRE support to enable PPTP support.
++
++  If you want to compile it as a module, say `M' here and read
++  Documentation/modules.txt.  If unsire, say `N'.
++
++RSH protocol support
++CONFIG_IP_NF_RSH
++  The RSH connection tracker is required if the dynamic
++  stderr "Server to Client" connection is to occur during a
++  normal RSH session.  This typically operates as follows;
++
++    Client 0:1023 --> Server 514    (stream 1 - stdin/stdout)
++    Client 0:1023 <-- Server 0:1023 (stream 2 - stderr)
++
++  This connection tracker will identify new RSH sessions,
++  extract the outbound session details, and notify netfilter
++  of pending "related" sessions.
++
++  Warning: This module could be dangerous. It is not "best
++           practice" to use RSH, use SSH in all instances.
++           (see rfc1244, rfc1948, rfc2179, etc ad-nauseum)
++
++
++  If you want to compile it as a module, say M here and read
++  <file: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
+@@ -2584,6 +2740,33 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++recent match support
++CONFIG_IP_NF_MATCH_RECENT
++  This match is used for creating one or many lists of recently
++  used addresses and then matching against that/those list(s).
++
++  Short options are available by using 'iptables -m recent -h'
++  Official Website: <http://snowman.net/projects/ipt_recent/>
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++quota match support
++CONFIG_IP_NF_MATCH_QUOTA
++  This match implements network quotas.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++addrtype match support
++CONFIG_IP_NF_MATCH_ADDRTYPE
++  This option allows you to match what routing thinks of an address,
++  eg. UNICAST, LOCAL, BROADCAST, ...
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
+ limit match support
+ CONFIG_IP_NF_MATCH_LIMIT
+   limit matching allows you to control the rate at which a rule can be
+@@ -2635,6 +2818,14 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++Multiple port with ranges match support
++CONFIG_IP_NF_MATCH_MPORT
++  This is an enhanced multiport match which supports port
++  ranges as well as single ports.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
+ Multiple port match support
+ CONFIG_IP_NF_MATCH_MULTIPORT
+   Multiport matching allows you to match TCP or UDP packets based on
+@@ -2652,6 +2843,18 @@
+   If you want to compile it as a module, say M here and read
+   Documentation/modules.txt.  If unsure, say `N'.
++U32 patch support
++CONFIG_IP_NF_MATCH_U32
++  U32 allows you to extract quantities of up to 4 bytes from a packet,
++  AND them with specified masks, shift them by specified amounts and
++  test whether the results are in any of a set of specified ranges.
++  The specification of what to extract is general enough to skip over
++  headers with lengths stored in the packet, as in IP or TCP header
++  lengths.
++
++  Details and examples are in the kernel module source.
++
++
+ LENGTH match support
+ CONFIG_IP_NF_MATCH_LENGTH
+   This option allows you to match the length of a packet against a
+@@ -2690,6 +2893,132 @@
+  
++Fuzzy Logic Controller match support
++CONFIG_IP_NF_MATCH_FUZZY
++  This option adds a `fuzzy' match,
++  which allows you to match packets according to a fuzzy logic
++  based law .
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++iprange match support
++CONFIG_IP_NF_MATCH_IPRANGE
++  This option makes possible to match IP addresses against
++  IP address ranges.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++
++IPV4OPTIONS patch support
++CONFIG_IP_NF_MATCH_IPV4OPTIONS
++  This option adds a IPV4OPTIONS match.
++  It allows you to filter options like source routing,
++  record route, timestamp and router-altert.
++
++  If you say Y here, try iptables -m ipv4options --help for more information.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++Nth match support
++CONFIG_IP_NF_MATCH_NTH
++  This option adds a `Nth' match, which allow you to make
++  rules that match every Nth packet.  By default there are 
++  16 different counters.
++
++[options]
++   --every     Nth              Match every Nth packet
++  [--counter]  num              Use counter 0-15 (default:0)
++  [--start]    num              Initialize the counter at the number 'num'
++                                instead of 0. Must be between 0 and Nth-1
++  [--packet]   num              Match on 'num' packet. Must be between 0
++                                and Nth-1.
++
++                                If --packet is used for a counter than
++                                there must be Nth number of --packet
++                                rules, covering all values between 0 and
++                                Nth-1 inclusively.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++OSF match support
++CONFIG_IP_NF_MATCH_OSF
++
++  The idea of passive OS fingerprint matching exists for quite a long time,
++  but was created as extension fo OpenBSD pf only some weeks ago.
++  Original idea was lurked in some OpenBSD mailing list (thanks
++  grange@open...) and than adopted for Linux netfilter in form of this code.
++
++  Original table was created by Michal Zalewski <lcamtuf@coredump.cx> for
++  his excellent p0f and than changed a bit for more convenience.
++
++  This module compares some data(WS, MSS, options and it's order, ttl,
++  df and others) from first SYN packet (actually from packets with SYN
++  bit set) with hardcoded in fingers[] table ones.
++
++  If you say Y here, try iptables -m osf --help for more information.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++psd match support
++CONFIG_IP_NF_MATCH_PSD
++  This option adds a `psd' match, which allows you to create rules in
++  any iptables table wich will detect TCP and UDP port scans.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++Random match support
++CONFIG_IP_NF_MATCH_RANDOM
++  This option adds a `random' match,
++  which allow you to match packets randomly
++  following a given probability.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++REALM match support
++CONFIG_IP_NF_MATCH_REALM
++  This option adds a `realm' match, which allows you to use the realm
++  key from the routing subsytem inside iptables.
++
++  This match pretty much resembles the CONFIG_NET_CLS_ROUTE4 option 
++  in tc world.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++TIME patch support
++CONFIG_IP_NF_MATCH_TIME
++  This option adds a `time' match, which allows you
++  to matchbased on the packet arrival time
++  (arrival time at the machine which the netfilter is running on) or
++  departure time (for locally generated packets).
++
++  If you say Y here, try iptables -m time --help for more information.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++Condition variable match support
++CONFIG_IP_NF_MATCH_CONDITION
++  This option allows you to match firewall rules against condition
++  variables stored in the /proc/net/ipt_condition directory.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
+ TOS match support
+ CONFIG_IP_NF_MATCH_TOS
+   TOS matching allows you to match packets based on the Type Of
+@@ -2710,6 +3039,44 @@
+   Documentation/modules.txt.  If unsure, say `N'.
++Connections/IP limit match support
++CONFIG_IP_NF_MATCH_CONNLIMIT
++  This match allows you to restrict the number of parallel TCP
++  connections to a server per client IP address (or address block).
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++RPC match support
++CONFIG_IP_NF_MATCH_RPC
++  This adds CONFIG_IP_NF_MATCH_RPC, which is the RPC connection
++  matcher and tracker.
++
++  This option supplies two connection tracking modules;
++  ip_conntrack_rpc_udp and ip_conntrack_rpc_tcp, which track
++  portmapper requests using UDP and TCP respectively.
++
++  This option also adds an RPC match module for iptables, which
++  matches both via the old "record match" method and a new
++  "procedure match" method. The older method matches all RPC
++  procedure packets that relate to previously recorded packets
++  seen querying a portmapper. The newer method matches only
++  those RPC procedure packets explicitly specified by the user,
++  and that can then be related to previously recorded packets
++  seen querying a portmapper.
++
++  These three modules are required if RPCs are to be filtered
++  accurately; as RPCs are allocated pseudo-randomly to UDP and
++  TCP ports as they register with the portmapper.
++
++  Up to 8 portmapper ports per module, and up to 128 RPC
++  procedures per iptables rule, may be specified by the user,
++  to enable effective RPC management.
++
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
+ Connection state match support
+ CONFIG_IP_NF_MATCH_STATE
+   Connection state matching allows you to match packets based on their
+@@ -2719,6 +3086,14 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++String match support (EXPERIMENTAL)
++CONFIG_IP_NF_MATCH_STRING
++  String matching alows you to match packets which contain a
++  specified string of characters.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
+ Unclean match support
+ CONFIG_IP_NF_MATCH_UNCLEAN
+   Unclean packet matching matches any strange or invalid packets, by
+@@ -2735,6 +3110,52 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++TARPIT target support
++CONFIG_IP_NF_TARGET_TARPIT
++  Adds a TARPIT target to iptables, which captures and holds
++  incoming TCP connections using no local per-connection resources.
++  Connections are accepted, but immediately switched to the persist
++  state (0 byte window), in which the remote side stops sending data
++  and asks to continue every 60-240 seconds.  Attempts to close the
++  connection are ignored, forcing the remote side to time out the
++  connection in 12-24 minutes.
++
++  This offers similar functionality to LaBrea
++  <http://www.hackbusters.net/LaBrea/> but doesn't require dedicated
++  hardware or IPs.  Any TCP port that you would normally DROP or REJECT
++  can instead become a tarpit.
++
++raw table support (required for NOTRACK/TRACE)
++CONFIG_IP_NF_RAW
++  This option adds a `raw' table to iptables. This table is the very
++  first in the netfilter framework and hooks in at the PREROUTING
++  and OUTPUT chains.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++TRACE target support
++CONFIG_IP_NF_TARGET_TRACE
++  The TRACE target allows packets to be traced as those
++  matches any subsequent rule in any table/rule. The matched
++  rule and the packet is logged with the prefix
++
++  TRACE: tablename/chainname/rulenum  
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++NOTRACK target support
++CONFIG_IP_NF_TARGET_NOTRACK
++  The NOTRACK target allows a select rule to specify
++  which packets *not* to enter the conntrack/NAT
++  subsystem with all the consequences (no ICMP error tracking,
++  no protocol helpers for the selected packets).
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++
+ Packet filtering
+ CONFIG_IP_NF_FILTER
+   Packet filtering defines a table `filter', which has a series of
+@@ -2744,6 +3165,24 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++IPV4OPTSSTRIP target support
++CONFIG_IP_NF_TARGET_IPV4OPTSSTRIP
++  This option adds an IPV4OPTSSTRIP target.
++  This target allows you to strip all IP options in a packet.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++NETLINK target support
++CONFIG_IP_NF_TARGET_NETLINK
++  The NETLINK target allows you to recieve packets in userspace via
++  the kernel firewall netlink socket. Apps such as fwmon
++  (http://firestorm.geek-ware.co.uk) can then recieve and dislpay
++  these packets. This option is basically a re-implementation of the
++  ipchains -o option.
++
++
+ REJECT target support
+ CONFIG_IP_NF_TARGET_REJECT
+   The REJECT target allows a filtering rule to specify that an ICMP
+@@ -2808,6 +3247,27 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++NETMAP target support
++CONFIG_IP_NF_TARGET_NETMAP
++  NETMAP is an implementation of static 1:1 NAT mapping of network
++  addresses. It maps the network address part, while keeping the
++  host address part intact. It is similar to Fast NAT, except that
++  Netfilter's connection tracking doesn't work well with Fast NAT.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  The module will be called
++  ipt_NETMAP.o.  If unsure, say `N'.
++
++SAME NAT target support
++CONFIG_IP_NF_TARGET_SAME
++  This option adds a `SAME' target, which works like the standard
++  SNAT target, but attempts to give clients the same IP for all
++  connections.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  The module will be called
++  ipt_SAME.o.  If unsure, say `N'.
++
+ REDIRECT target support
+ CONFIG_IP_NF_TARGET_REDIRECT
+   REDIRECT is a special case of NAT: all incoming connections are
+@@ -2866,6 +3326,42 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++IMQ target support
++CONFIG_IP_NF_TARGET_IMQ
++  This option adds a `IMQ' target which is used to specify if and
++  to which imq device packets should get enqueued/dequeued.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++IPMARK target support
++CONFIG_IP_NF_TARGET_IPMARK
++  This option adds a `IPMARK' target, which allows you to create rules
++  in the `mangle' table which alter the netfilter mark (nfmark) field
++  basing on the source or destination ip address of the packet.
++  This is very useful for very fast massive mangling and marking.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++
++
++ROUTE target support
++CONFIG_IP_NF_TARGET_ROUTE
++  This option adds a `ROUTE' target, which enables you to setup unusual
++  routes. For example, the ROUTE lets you route a received packet through 
++  an interface or towards a host, even if the regular destination of the 
++  packet is the router itself. The ROUTE target is also able to change the 
++  incoming interface of a packet.
++
++  The target can be or not a final target. It has to be used inside the 
++  mangle table.
++  
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  The module will be called ipt_ROUTE.o.
++  If unsure, say `N'.
++
++
+ MARK target support
+ CONFIG_IP_NF_TARGET_MARK
+   This option adds a `MARK' target, which allows you to create rules
+@@ -2933,6 +3429,73 @@
+   If you want to compile it as a module, say M here and read
+   Documentation/modules.txt.  If unsure, say `N'.
++TTL target support
++CONFIG_IP_NF_TARGET_TTL
++  This option adds a `TTL' target, which enables the user to set
++  the TTL value or increment / decrement the TTL value by a given
++  amount.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++pool match and target support
++CONFIG_IP_NF_MATCH_POOL
++  Pool matching lets you use bitmaps with one bit per address from some
++  range of IP addresses; the match depends on whether a checked source
++  or destination address has its bit set in the pool.
++
++  There is also a POOL netfilter target, which can be used to set or remove
++  the addresses of a packet from a pool.
++
++  To define and use pools, you need userlevel utilities: a patched iptables,
++  and the program ippool(8), which defines the pools and their bounds.
++  The current release of pool matching is ippool-0.0.2, and can be found
++  in the archives of the netfilter mailing list at
++  http://lists.samba.org/netfilter/.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++pool match and target statistics gathering
++CONFIG_IP_POOL_STATISTICS
++  This option controls whether usage gathering code is compiled into the
++  ip_pool module.  Disabling statistics may be substantially faster.
++
++CLASSIFY target support
++CONFIG_IP_NF_TARGET_CLASSIFY
++  This option adds a `CLASSIFY' target, which enables the user to set
++  the priority of a packet. Some qdiscs can use this value for classification,
++  among these are:
++
++  atm, cbq, dsmark, pfifo_fast, htb, prio
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++TCPLAG target support
++CONFIG_IP_NF_TARGET_TCPLAG
++  This option adds a `TCPLAG' target, intended for INPUT, OUTPUT and
++  FORWARD chains.
++
++  This target has no effect on packets but will passively monitor TCP/IP
++  connections and send lag estimates to syslog. Lag estimates are
++  generated by considering the time delay between SEQ and matching ACK,
++  which does not map precisely to any particular network property.
++  We can say that a fast network will typically give smaller lag values
++  than a slow network.
++
++  Safest option is to choose `M' here and compile as a module,
++  the module will do nothing until activated using the `iptables' utility.
++
++
++XOR target support
++CONFIG_IP_NF_TARGET_XOR
++  This option adds a `XOR' target, which can encrypt TCP and 
++  UDP traffic using a simple XOR encryption.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
+ LOG target support
+ CONFIG_IP_NF_TARGET_LOG
+   This option adds a `LOG' target, which allows you to create rules in
+@@ -2972,6 +3535,93 @@
+   If you want to compile it as a module, say M here and read
+   Documentation/modules.txt.  If unsure, say `N'.
++AH/ESP match support (EXPERIMENTAL)
++CONFIG_IP6_NF_MATCH_AHESP
++  These two match extensions (`ah' and `esp') allow you to match a
++  range of SPIs inside AH or ESP headers of IPv6 packets.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++Fragmentation header match support (EXPERIMENTAL)
++CONFIG_IP6_NF_MATCH_FRAG
++  This match extension (`frag') allow you to select the packet based on the
++  fileds of the fragmentation header of the IPv6 packets.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++IPv6 Extension Headers Match (EXPERIMENTAL)
++CONFIG_IP6_NF_MATCH_IPV6HEADER
++  extension header matching allows you to controll the packets based
++  on their extension headers.
++  
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++Fragmentation header match support (EXPERIMENTAL)
++CONFIG_IP6_NF_MATCH_OPTS
++  These match extensions (`hbh' and `dst') allow you to select the packet 
++  based on the fileds of the option header of the IPv6 packets.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++Fragmentation header match support (EXPERIMENTAL)
++CONFIG_IP6_NF_MATCH_RT
++  This match extension (`rt') allow you to select the packet based on the
++  fileds of the routing header of the IPv6 packets.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++Fuzzy Logic Controller match support
++CONFIG_IP6_NF_MATCH_FUZZY
++  This option adds a `fuzzy' match, which allows you to match
++  packets according to a fuzzy logic based law.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++Nth match support
++CONFIG_IP6_NF_MATCH_NTH
++  This option adds a `Nth' match, which allow you to make
++  rules that match every Nth packet.  By default there are 
++  16 different counters.
++
++[options]
++   --every     Nth              Match every Nth packet
++  [--counter]  num              Use counter 0-15 (default:0)
++  [--start]    num              Initialize the counter at the number 'num'
++                                instead of 0. Must be between 0 and Nth-1
++  [--packet]   num              Match on 'num' packet. Must be between 0
++                                and Nth-1.
++
++                                If --packet is used for a counter than
++                                there must be Nth number of --packet
++                                rules, covering all values between 0 and
++                                Nth-1 inclusively.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
++Random match support
++CONFIG_IP6_NF_MATCH_RANDOM
++  This option adds a `random' match,
++  which allow you to match packets randomly
++  following a given probability.
++ 
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++
+ MAC address match support
+ CONFIG_IP6_NF_MATCH_MAC
+   mac matching allows you to match packets based on the source
+@@ -2988,6 +3638,14 @@
+   If you want to compile it as a module, say M here and read
+   Documentation/modules.txt.  If unsure, say `N'.
++Condition variable match support
++CONFIG_IP6_NF_MATCH_CONDITION
++  This option allows you to match firewall rules against condition
++  variables stored in the /proc/net/ipt_condition directory.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
+ Netfilter MARK match support
+ CONFIG_IP6_NF_MATCH_MARK
+   Netfilter mark matching allows you to match packets based on the
+@@ -3031,6 +3689,35 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++REJECT target support
++CONFIG_IP6_NF_TARGET_REJECT
++  The REJECT target allows a filtering rule to specify that an ICMPv6
++  error should be issued in response to an incoming packet, rather
++  than silently being dropped.
++
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt.  If unsure, say `N'.
++
++raw table support (required for TRACE)
++CONFIG_IP6_NF_RAW
++  This option adds a `raw' table to ip6tables. This table is the very
++  first in the netfilter framework and hooks in at the PREROUTING
++  and OUTPUT chains.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++TRACE target support
++CONFIG_IP6_NF_TARGET_TRACE
++  The TRACE target allows packets to be traced as those
++  matches any subsequent rule in any table/rule. The matched
++  rule and the packet is logged with the prefix
++
++  TRACE: tablename/chainname/rulenum  
++ 
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
+ Packet filtering
+ CONFIG_IP6_NF_FILTER
+   Packet filtering defines a table `filter', which has a series of
+@@ -3049,6 +3736,26 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++IMQ target support
++CONFIG_IP6_NF_TARGET_IMQ
++  This option adds a `IMQ' target which is used to specify if and
++  to which imq device packets should get enqueued/dequeued.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++ROUTE target support
++CONFIG_IP6_NF_TARGET_ROUTE
++  This option adds a `ROUTE' target, which enables you to setup unusual
++  routes. The ROUTE target is also able to change the incoming interface
++  of a packet.
++
++  The target can be or not a final target. It has to be used inside the 
++  mangle table.
++  
++  Not working as a module.
++
++
+ MARK target support
+ CONFIG_IP6_NF_TARGET_MARK
+   This option adds a `MARK' target, which allows you to create rules
+@@ -3061,6 +3768,11 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++ARP payload mangling
++CONFIG_IP_NF_ARP_MANGLE
++  Allows altering the ARP packet payload: source and destination
++  hardware and network addresses.
++
+ TCP Explicit Congestion Notification support
+ CONFIG_INET_ECN
+   Explicit Congestion Notification (ECN) allows routers to notify
+@@ -3096,6 +3808,22 @@
+   If you want to compile it as a module, say M here and read
+   <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++HL match support
++CONFIG_IP6_NF_MATCH_HL
++  This option adds a `hl' match, which allows you match the value of
++  the IPv6 Hop Limit field.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++HL target support
++CONFIG_IP6_NF_TARGET_HL
++  This option adds a `HL' target, which allows you to modify the value of
++  IPv6 Hop Limit field.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
+ LOG target support
+ CONFIG_IP6_NF_TARGET_LOG
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/jhash.h linux-2.4.20/include/linux/jhash.h
+--- linux-2.4.20.org/include/linux/jhash.h     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/jhash.h Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,143 @@
++#ifndef _LINUX_JHASH_H
++#define _LINUX_JHASH_H
++
++/* jhash.h: Jenkins hash support.
++ *
++ * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net)
++ *
++ * http://burtleburtle.net/bob/hash/
++ *
++ * These are the credits from Bob's sources:
++ *
++ * lookup2.c, by Bob Jenkins, December 1996, Public Domain.
++ * hash(), hash2(), hash3, and mix() are externally useful functions.
++ * Routines to test the hash are included if SELF_TEST is defined.
++ * You can use this free for any purpose.  It has no warranty.
++ *
++ * Copyright (C) 2003 David S. Miller (davem@redhat.com)
++ *
++ * I've modified Bob's hash to be useful in the Linux kernel, and
++ * any bugs present are surely my fault.  -DaveM
++ */
++
++/* NOTE: Arguments are modified. */
++#define __jhash_mix(a, b, c) \
++{ \
++  a -= b; a -= c; a ^= (c>>13); \
++  b -= c; b -= a; b ^= (a<<8); \
++  c -= a; c -= b; c ^= (b>>13); \
++  a -= b; a -= c; a ^= (c>>12);  \
++  b -= c; b -= a; b ^= (a<<16); \
++  c -= a; c -= b; c ^= (b>>5); \
++  a -= b; a -= c; a ^= (c>>3);  \
++  b -= c; b -= a; b ^= (a<<10); \
++  c -= a; c -= b; c ^= (b>>15); \
++}
++
++/* The golden ration: an arbitrary value */
++#define JHASH_GOLDEN_RATIO    0x9e3779b9
++
++/* The most generic version, hashes an arbitrary sequence
++ * of bytes.  No alignment or length assumptions are made about
++ * the input key.
++ */
++static inline u32 jhash(void *key, u32 length, u32 initval)
++{
++      u32 a, b, c, len;
++      u8 *k = key;
++
++      len = length;
++      a = b = JHASH_GOLDEN_RATIO;
++      c = initval;
++
++      while (len >= 12) {
++              a += (k[0] +((u32)k[1]<<8) +((u32)k[2]<<16) +((u32)k[3]<<24));
++              b += (k[4] +((u32)k[5]<<8) +((u32)k[6]<<16) +((u32)k[7]<<24));
++              c += (k[8] +((u32)k[9]<<8) +((u32)k[10]<<16)+((u32)k[11]<<24));
++
++              __jhash_mix(a,b,c);
++
++              k += 12;
++              len -= 12;
++      }
++
++      c += length;
++      switch (len) {
++      case 11: c += ((u32)k[10]<<24);
++      case 10: c += ((u32)k[9]<<16);
++      case 9 : c += ((u32)k[8]<<8);
++      case 8 : b += ((u32)k[7]<<24);
++      case 7 : b += ((u32)k[6]<<16);
++      case 6 : b += ((u32)k[5]<<8);
++      case 5 : b += k[4];
++      case 4 : a += ((u32)k[3]<<24);
++      case 3 : a += ((u32)k[2]<<16);
++      case 2 : a += ((u32)k[1]<<8);
++      case 1 : a += k[0];
++      };
++
++      __jhash_mix(a,b,c);
++
++      return c;
++}
++
++/* A special optimized version that handles 1 or more of u32s.
++ * The length parameter here is the number of u32s in the key.
++ */
++static inline u32 jhash2(u32 *k, u32 length, u32 initval)
++{
++      u32 a, b, c, len;
++
++      a = b = JHASH_GOLDEN_RATIO;
++      c = initval;
++      len = length;
++
++      while (len >= 3) {
++              a += k[0];
++              b += k[1];
++              c += k[2];
++              __jhash_mix(a, b, c);
++              k += 3; len -= 3;
++      }
++
++      c += length * 4;
++
++      switch (len) {
++      case 2 : b += k[1];
++      case 1 : a += k[0];
++      };
++
++      __jhash_mix(a,b,c);
++
++      return c;
++}
++
++
++/* A special ultra-optimized versions that knows they are hashing exactly
++ * 3, 2 or 1 word(s).
++ *
++ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally
++ *       done at the end is not done here.
++ */
++static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
++{
++      a += JHASH_GOLDEN_RATIO;
++      b += JHASH_GOLDEN_RATIO;
++      c += initval;
++
++      __jhash_mix(a, b, c);
++
++      return c;
++}
++
++static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
++{
++      return jhash_3words(a, b, 0, initval);
++}
++
++static inline u32 jhash_1word(u32 a, u32 initval)
++{
++      return jhash_3words(a, 0, 0, initval);
++}
++
++#endif /* _LINUX_JHASH_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter.h linux-2.4.20/include/linux/netfilter.h
+--- linux-2.4.20.org/include/linux/netfilter.h Thu Nov 22 19:47:48 2001
++++ linux-2.4.20/include/linux/netfilter.h     Wed Sep 24 09:18:12 2003
+@@ -19,9 +19,11 @@
+ #define NF_REPEAT 4
+ #define NF_MAX_VERDICT NF_REPEAT
+-/* Generic cache responses from hook functions. */
+-#define NFC_ALTERED 0x8000
++/* Generic cache responses from hook functions.
++   <= 0x2000 is used for protocol-flags. */
+ #define NFC_UNKNOWN 0x4000
++#define NFC_ALTERED 0x8000
++#define NFC_TRACE   0x10000
+ #ifdef __KERNEL__
+ #include <linux/config.h>
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_arp/arpt_mangle.h linux-2.4.20/include/linux/netfilter_arp/arpt_mangle.h
+--- linux-2.4.20.org/include/linux/netfilter_arp/arpt_mangle.h Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_arp/arpt_mangle.h     Wed Sep 24 09:16:17 2003
+@@ -0,0 +1,26 @@
++#ifndef _ARPT_MANGLE_H
++#define _ARPT_MANGLE_H
++#include <linux/netfilter_arp/arp_tables.h>
++
++#define ARPT_MANGLE_ADDR_LEN_MAX sizeof(struct in_addr)
++struct arpt_mangle
++{
++      char src_devaddr[ARPT_DEV_ADDR_LEN_MAX];
++      char tgt_devaddr[ARPT_DEV_ADDR_LEN_MAX];
++      union {
++              struct in_addr src_ip;
++      } u_s;
++      union {
++              struct in_addr tgt_ip;
++      } u_t;
++      u_int8_t flags;
++      int target;
++};
++
++#define ARPT_MANGLE_SDEV 0x01
++#define ARPT_MANGLE_TDEV 0x02
++#define ARPT_MANGLE_SIP 0x04
++#define ARPT_MANGLE_TIP 0x08
++#define ARPT_MANGLE_MASK 0x0f
++
++#endif /* _ARPT_MANGLE_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack.h       Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack.h   Wed Sep 24 09:18:16 2003
+@@ -6,6 +6,7 @@
+ #include <linux/config.h>
+ #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
++#include <linux/bitops.h>
+ #include <asm/atomic.h>
+ enum ip_conntrack_info
+@@ -41,29 +42,50 @@
+       /* Conntrack should never be early-expired. */
+       IPS_ASSURED_BIT = 2,
+       IPS_ASSURED = (1 << IPS_ASSURED_BIT),
++
++      /* Connection is confirmed: originating packet has left box */
++      IPS_CONFIRMED_BIT = 3,
++      IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT),
+ };
+ #include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
+ #include <linux/netfilter_ipv4/ip_conntrack_icmp.h>
++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
+ /* per conntrack: protocol private data */
+ union ip_conntrack_proto {
+       /* insert conntrack proto private data here */
++      struct ip_ct_gre gre;
+       struct ip_ct_tcp tcp;
+       struct ip_ct_icmp icmp;
+ };
+ union ip_conntrack_expect_proto {
+       /* insert expect proto private data here */
++      struct ip_ct_gre_expect gre;
+ };
+ /* Add protocol helper include file here */
++#include <linux/netfilter_ipv4/ip_conntrack_talk.h>
++#include <linux/netfilter_ipv4/ip_conntrack_rsh.h>
++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
++#include <linux/netfilter_ipv4/ip_conntrack_mms.h>
++#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
++
++#include <linux/netfilter_ipv4/ip_conntrack_amanda.h>
++
+ #include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
+ #include <linux/netfilter_ipv4/ip_conntrack_irc.h>
+ /* per expectation: application helper private data */
+ union ip_conntrack_expect_help {
+       /* insert conntrack helper private data (expect) here */
++      struct ip_ct_talk_expect exp_talk_info;
++      struct ip_ct_rsh_expect exp_rsh_info;
++      struct ip_ct_pptp_expect exp_pptp_info;
++      struct ip_ct_mms_expect exp_mms_info;
++      struct ip_ct_h225_expect exp_h225_info;
++      struct ip_ct_amanda_expect exp_amanda_info;
+       struct ip_ct_ftp_expect exp_ftp_info;
+       struct ip_ct_irc_expect exp_irc_info;
+@@ -77,16 +99,23 @@
+ /* per conntrack: application helper private data */
+ union ip_conntrack_help {
+       /* insert conntrack helper private data (master) here */
++      struct ip_ct_talk_master ct_talk_info;
++      struct ip_ct_rsh_master ct_rsh_info;
++      struct ip_ct_pptp_master ct_pptp_info;
++      struct ip_ct_mms_master ct_mms_info;
++      struct ip_ct_h225_master ct_h225_info;
+       struct ip_ct_ftp_master ct_ftp_info;
+       struct ip_ct_irc_master ct_irc_info;
+ };
+ #ifdef CONFIG_IP_NF_NAT_NEEDED
+ #include <linux/netfilter_ipv4/ip_nat.h>
++#include <linux/netfilter_ipv4/ip_nat_pptp.h>
+ /* per conntrack: nat application helper private data */
+ union ip_conntrack_nat_help {
+       /* insert nat helper private data here */
++      struct ip_nat_pptp nat_pptp_info;
+ };
+ #endif
+@@ -159,7 +188,7 @@
+       struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
+       /* Have we seen traffic both ways yet? (bitset) */
+-      volatile unsigned long status;
++      unsigned long status;
+       /* Timer function; drops refcnt when it goes off. */
+       struct timer_list timeout;
+@@ -198,6 +227,9 @@
+       } nat;
+ #endif /* CONFIG_IP_NF_NAT_NEEDED */
++#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
++      unsigned long mark;
++#endif
+ };
+ /* get master conntrack via master expectation */
+@@ -238,6 +270,9 @@
+ extern void ip_ct_refresh(struct ip_conntrack *ct,
+                         unsigned long extra_jiffies);
++/* Kill conntrack */
++extern void ip_ct_death_by_timeout(unsigned long ul_conntrack);
++
+ /* These are for NAT.  Icky. */
+ /* Call me when a conntrack is destroyed. */
+ extern void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack);
+@@ -254,9 +289,12 @@
+ /* It's confirmed if it is, or has been in the hash table. */
+ static inline int is_confirmed(struct ip_conntrack *ct)
+ {
+-      return ct->tuplehash[IP_CT_DIR_ORIGINAL].list.next != NULL;
++      return test_bit(IPS_CONFIRMED_BIT, &ct->status);
+ }
+ extern unsigned int ip_conntrack_htable_size;
++
++/* A fake conntrack entry which never vanishes. */
++extern struct ip_conntrack ip_conntrack_untracked;
+ #endif /* __KERNEL__ */
+ #endif /* _IP_CONNTRACK_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_amanda.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_amanda.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_amanda.h        Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_amanda.h    Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,29 @@
++#ifndef _IP_CONNTRACK_AMANDA_H
++#define _IP_CONNTRACK_AMANDA_H
++/* AMANDA tracking. */
++
++#ifdef __KERNEL__
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++
++/* Protects amanda part of conntracks */
++DECLARE_LOCK_EXTERN(ip_amanda_lock);
++
++#endif
++
++struct conn {
++      char* match;
++      int matchlen;
++};
++
++#define NUM_MSGS      3
++
++
++struct ip_ct_amanda_expect
++{
++      u_int16_t port;         /* port number of this expectation */
++      u_int16_t offset;       /* offset of the port specification in ctrl packet */
++      u_int16_t len;          /* the length of the port number specification */
++};
++
++#endif /* _IP_CONNTRACK_AMANDA_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_core.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_core.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_core.h  Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_core.h      Wed Sep 24 09:16:17 2003
+@@ -1,5 +1,6 @@
+ #ifndef _IP_CONNTRACK_CORE_H
+ #define _IP_CONNTRACK_CORE_H
++#include <linux/netfilter.h>
+ #include <linux/netfilter_ipv4/lockhelp.h>
+ /* This header is used to share core functionality between the
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h       Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h   Wed Sep 24 09:17:38 2003
+@@ -0,0 +1,70 @@
++#ifndef _IP_CT_CUSEEME
++#define _IP_CT_CUSEEME
++
++#define CUSEEME_PORT 7648
++
++/* These structs come from the 2.2 ip_masq_cuseeme code... */
++
++#pragma pack(1)
++/* CuSeeMe data header */
++struct cu_header {
++      u_int16_t       dest_family;
++      u_int16_t       dest_port;
++      u_int32_t       dest_addr;
++      int16_t         family;
++      u_int16_t       port;
++      u_int32_t       addr;
++      u_int32_t       seq;
++      u_int16_t       msg;
++      u_int16_t       data_type;
++                              /* possible values:
++                               * 1    small video
++                               * 2    big video
++                               * 3    audio
++                               * 100  acknowledge connectivity when there
++                               *      is nothing else to send
++                               * 101  OpenContinue packet
++                               * 104  display a text message and 
++                               *      disconnect (used by reflector to
++                               *      kick clients off)
++                               * 105  display a text message (welcome
++                               *      message from reflector)
++                               * 106  exchanged among reflectors for
++                               *      reflector interoperation
++                               * 107  carry aux stream data when there is
++                               *      no video to piggy-back on
++                               * 108  obsolete (used in Mac alpha version)
++                               * 109  obsolete (used in Mac alpha version)
++                               * 110  used for data rate control
++                               * 111  used for data rate control
++                               * 256  aux data control messages
++                               * 257  aux data packets
++                               * */
++      u_int16_t       packet_len;
++};
++
++/* Open Continue Header */
++struct oc_header {
++      struct cu_header        cu_head;
++      u_int16_t       client_count; /* Number of client info structs */
++      u_int32_t       seq_no;
++      char            user_name[20];
++      char            stuff[4];     /* Flags, version stuff, etc */
++};
++
++/* Client info structures */
++struct client_info {
++      u_int32_t       address;      /* Client address */
++      char            stuff[8];     /* Flags, pruning bitfield, packet counts, etc */
++};
++#pragma pack()
++
++/* This structure is per expected connection */
++struct ip_ct_cuseeme_expect {
++};
++
++/* This structure exists only once per master */
++struct ip_ct_cuseeme_master {
++};
++
++#endif /* _IP_CT_CUSEEME */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_h323.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_h323.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_h323.h  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_h323.h      Wed Sep 24 09:17:43 2003
+@@ -0,0 +1,30 @@
++#ifndef _IP_CONNTRACK_H323_H
++#define _IP_CONNTRACK_H323_H
++/* H.323 connection tracking. */
++
++#ifdef __KERNEL__
++/* Protects H.323 related data */
++DECLARE_LOCK_EXTERN(ip_h323_lock);
++#endif
++
++/* Default H.225 port */
++#define H225_PORT     1720
++
++/* This structure is per expected connection */
++struct ip_ct_h225_expect {
++      u_int16_t port;                 /* Port of the H.225 helper/RTCP/RTP channel */
++      enum ip_conntrack_dir dir;      /* Direction of the original connection */
++      unsigned int offset;            /* offset of the address in the payload */
++};
++
++/* This structure exists only once per master */
++struct ip_ct_h225_master {
++      int is_h225;                            /* H.225 or H.245 connection */
++#ifdef CONFIG_IP_NF_NAT_NEEDED
++      enum ip_conntrack_dir dir;              /* Direction of the original connection */
++      u_int32_t seq[IP_CT_DIR_MAX];           /* Exceptional packet mangling for signal addressess... */
++      unsigned int offset[IP_CT_DIR_MAX];     /* ...and the offset of the addresses in the payload */
++#endif
++};
++
++#endif /* _IP_CONNTRACK_H323_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_mms.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_mms.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_mms.h   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_mms.h       Wed Sep 24 09:17:48 2003
+@@ -0,0 +1,31 @@
++#ifndef _IP_CONNTRACK_MMS_H
++#define _IP_CONNTRACK_MMS_H
++/* MMS tracking. */
++
++#ifdef __KERNEL__
++#include <linux/netfilter_ipv4/lockhelp.h>
++
++DECLARE_LOCK_EXTERN(ip_mms_lock);
++
++#define MMS_PORT                         1755
++#define MMS_SRV_MSG_ID                   196610
++
++#define MMS_SRV_MSG_OFFSET               36
++#define MMS_SRV_UNICODE_STRING_OFFSET    60
++#define MMS_SRV_CHUNKLENLV_OFFSET        16
++#define MMS_SRV_CHUNKLENLM_OFFSET        32
++#define MMS_SRV_MESSAGELENGTH_OFFSET     8
++#endif
++
++/* This structure is per expected connection */
++struct ip_ct_mms_expect {
++      u_int32_t len;
++      u_int32_t padding;
++      u_int16_t port;
++};
++
++/* This structure exists only once per master */
++struct ip_ct_mms_master {
++};
++
++#endif /* _IP_CONNTRACK_MMS_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_pptp.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_pptp.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_pptp.h  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_pptp.h      Wed Sep 24 09:17:55 2003
+@@ -0,0 +1,313 @@
++/* PPTP constants and structs */
++#ifndef _CONNTRACK_PPTP_H
++#define _CONNTRACK_PPTP_H
++
++/* state of the control session */
++enum pptp_ctrlsess_state {
++      PPTP_SESSION_NONE,                      /* no session present */
++      PPTP_SESSION_ERROR,                     /* some session error */
++      PPTP_SESSION_STOPREQ,                   /* stop_sess request seen */
++      PPTP_SESSION_REQUESTED,                 /* start_sess request seen */
++      PPTP_SESSION_CONFIRMED,                 /* session established */
++};
++
++/* state of the call inside the control session */
++enum pptp_ctrlcall_state {
++      PPTP_CALL_NONE,
++      PPTP_CALL_ERROR,
++      PPTP_CALL_OUT_REQ,
++      PPTP_CALL_OUT_CONF,
++      PPTP_CALL_IN_REQ,
++      PPTP_CALL_IN_REP,
++      PPTP_CALL_IN_CONF,
++      PPTP_CALL_CLEAR_REQ,
++};
++
++
++/* conntrack private data */
++struct ip_ct_pptp_master {
++      enum pptp_ctrlsess_state sstate;        /* session state */
++
++      /* everything below is going to be per-expectation in newnat,
++       * since there could be more than one call within one session */
++      enum pptp_ctrlcall_state cstate;        /* call state */
++      u_int16_t pac_call_id;                  /* call id of PAC, host byte order */
++      u_int16_t pns_call_id;                  /* call id of PNS, host byte order */
++};
++
++/* conntrack_expect private member */
++struct ip_ct_pptp_expect {
++      enum pptp_ctrlcall_state cstate;        /* call state */
++      u_int16_t pac_call_id;                  /* call id of PAC */
++      u_int16_t pns_call_id;                  /* call id of PNS */
++};
++
++
++#ifdef __KERNEL__
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++DECLARE_LOCK_EXTERN(ip_pptp_lock);
++
++#define IP_CONNTR_PPTP                PPTP_CONTROL_PORT
++
++union pptp_ctrl_union {
++                void                          *rawreq;
++              struct PptpStartSessionRequest  *sreq;
++              struct PptpStartSessionReply    *srep;
++              struct PptpStopSessionReqest    *streq;
++              struct PptpStopSessionReply     *strep;
++                struct PptpOutCallRequest       *ocreq;
++                struct PptpOutCallReply         *ocack;
++                struct PptpInCallRequest        *icreq;
++                struct PptpInCallReply          *icack;
++                struct PptpInCallConnected      *iccon;
++              struct PptpClearCallRequest     *clrreq;
++                struct PptpCallDisconnectNotify *disc;
++                struct PptpWanErrorNotify       *wanerr;
++                struct PptpSetLinkInfo          *setlink;
++};
++
++
++
++#define PPTP_CONTROL_PORT     1723
++
++#define PPTP_PACKET_CONTROL   1
++#define PPTP_PACKET_MGMT      2
++
++#define PPTP_MAGIC_COOKIE     0x1a2b3c4d
++
++struct pptp_pkt_hdr {
++      __u16   packetLength;
++      __u16   packetType;
++      __u32   magicCookie;
++};
++
++/* PptpControlMessageType values */
++#define PPTP_START_SESSION_REQUEST    1
++#define PPTP_START_SESSION_REPLY      2
++#define PPTP_STOP_SESSION_REQUEST     3
++#define PPTP_STOP_SESSION_REPLY               4
++#define PPTP_ECHO_REQUEST             5
++#define PPTP_ECHO_REPLY                       6
++#define PPTP_OUT_CALL_REQUEST         7
++#define PPTP_OUT_CALL_REPLY           8
++#define PPTP_IN_CALL_REQUEST          9
++#define PPTP_IN_CALL_REPLY            10
++#define PPTP_IN_CALL_CONNECT          11
++#define PPTP_CALL_CLEAR_REQUEST               12
++#define PPTP_CALL_DISCONNECT_NOTIFY   13
++#define PPTP_WAN_ERROR_NOTIFY         14
++#define PPTP_SET_LINK_INFO            15
++
++#define PPTP_MSG_MAX                  15
++
++/* PptpGeneralError values */
++#define PPTP_ERROR_CODE_NONE          0
++#define PPTP_NOT_CONNECTED            1
++#define PPTP_BAD_FORMAT                       2
++#define PPTP_BAD_VALUE                        3
++#define PPTP_NO_RESOURCE              4
++#define PPTP_BAD_CALLID                       5
++#define PPTP_REMOVE_DEVICE_ERROR      6
++
++struct PptpControlHeader {
++      __u16   messageType;
++      __u16   reserved;
++};
++
++/* FramingCapability Bitmap Values */
++#define PPTP_FRAME_CAP_ASYNC          0x1
++#define PPTP_FRAME_CAP_SYNC           0x2
++
++/* BearerCapability Bitmap Values */
++#define PPTP_BEARER_CAP_ANALOG                0x1
++#define PPTP_BEARER_CAP_DIGITAL               0x2
++
++struct PptpStartSessionRequest {
++      __u16   protocolVersion;
++      __u8    reserved1;
++      __u8    reserved2;
++      __u32   framingCapability;
++      __u32   bearerCapability;
++      __u16   maxChannels;
++      __u16   firmwareRevision;
++      __u8    hostName[64];
++      __u8    vendorString[64];
++};
++
++/* PptpStartSessionResultCode Values */
++#define PPTP_START_OK                 1
++#define PPTP_START_GENERAL_ERROR      2
++#define PPTP_START_ALREADY_CONNECTED  3
++#define PPTP_START_NOT_AUTHORIZED     4
++#define PPTP_START_UNKNOWN_PROTOCOL   5
++
++struct PptpStartSessionReply {
++      __u16   protocolVersion;
++      __u8    resultCode;
++      __u8    generalErrorCode;
++      __u32   framingCapability;
++      __u32   bearerCapability;
++      __u16   maxChannels;
++      __u16   firmwareRevision;
++      __u8    hostName[64];
++      __u8    vendorString[64];
++};
++
++/* PptpStopReasons */
++#define PPTP_STOP_NONE                        1
++#define PPTP_STOP_PROTOCOL            2
++#define PPTP_STOP_LOCAL_SHUTDOWN      3
++
++struct PptpStopSessionRequest {
++      __u8    reason;
++};
++
++/* PptpStopSessionResultCode */
++#define PPTP_STOP_OK                  1
++#define PPTP_STOP_GENERAL_ERROR               2
++
++struct PptpStopSessionReply {
++      __u8    resultCode;
++      __u8    generalErrorCode;
++};
++
++struct PptpEchoRequest {
++      __u32 identNumber;
++};
++
++/* PptpEchoReplyResultCode */
++#define PPTP_ECHO_OK                  1
++#define PPTP_ECHO_GENERAL_ERROR               2
++
++struct PptpEchoReply {
++      __u32   identNumber;
++      __u8    resultCode;
++      __u8    generalErrorCode;
++      __u16   reserved;
++};
++
++/* PptpFramingType */
++#define PPTP_ASYNC_FRAMING            1
++#define PPTP_SYNC_FRAMING             2
++#define PPTP_DONT_CARE_FRAMING                3
++
++/* PptpCallBearerType */
++#define PPTP_ANALOG_TYPE              1
++#define PPTP_DIGITAL_TYPE             2
++#define PPTP_DONT_CARE_BEARER_TYPE    3
++
++struct PptpOutCallRequest {
++      __u16   callID;
++      __u16   callSerialNumber;
++      __u32   minBPS;
++      __u32   maxBPS;
++      __u32   bearerType;
++      __u32   framingType;
++      __u16   packetWindow;
++      __u16   packetProcDelay;
++      __u16   reserved1;
++      __u16   phoneNumberLength;
++      __u16   reserved2;
++      __u8    phoneNumber[64];
++      __u8    subAddress[64];
++};
++
++/* PptpCallResultCode */
++#define PPTP_OUTCALL_CONNECT          1
++#define PPTP_OUTCALL_GENERAL_ERROR    2
++#define PPTP_OUTCALL_NO_CARRIER               3
++#define PPTP_OUTCALL_BUSY             4
++#define PPTP_OUTCALL_NO_DIAL_TONE     5
++#define PPTP_OUTCALL_TIMEOUT          6
++#define PPTP_OUTCALL_DONT_ACCEPT      7
++
++struct PptpOutCallReply {
++      __u16   callID;
++      __u16   peersCallID;
++      __u8    resultCode;
++      __u8    generalErrorCode;
++      __u16   causeCode;
++      __u32   connectSpeed;
++      __u16   packetWindow;
++      __u16   packetProcDelay;
++      __u32   physChannelID;
++};
++
++struct PptpInCallRequest {
++      __u16   callID;
++      __u16   callSerialNumber;
++      __u32   callBearerType;
++      __u32   physChannelID;
++      __u16   dialedNumberLength;
++      __u16   dialingNumberLength;
++      __u8    dialedNumber[64];
++      __u8    dialingNumber[64];
++      __u8    subAddress[64];
++};
++
++/* PptpInCallResultCode */
++#define PPTP_INCALL_ACCEPT            1
++#define PPTP_INCALL_GENERAL_ERROR     2
++#define PPTP_INCALL_DONT_ACCEPT               3
++
++struct PptpInCallReply {
++      __u16   callID;
++      __u16   peersCallID;
++      __u8    resultCode;
++      __u8    generalErrorCode;
++      __u16   packetWindow;
++      __u16   packetProcDelay;
++      __u16   reserved;
++};
++
++struct PptpInCallConnected {
++      __u16   peersCallID;
++      __u16   reserved;
++      __u32   connectSpeed;
++      __u16   packetWindow;
++      __u16   packetProcDelay;
++      __u32   callFramingType;
++};
++
++struct PptpClearCallRequest {
++      __u16   callID;
++      __u16   reserved;
++};
++
++struct PptpCallDisconnectNotify {
++      __u16   callID;
++      __u8    resultCode;
++      __u8    generalErrorCode;
++      __u16   causeCode;
++      __u16   reserved;
++      __u8    callStatistics[128];
++};
++
++struct PptpWanErrorNotify {
++      __u16   peersCallID;
++      __u16   reserved;
++      __u32   crcErrors;
++      __u32   framingErrors;
++      __u32   hardwareOverRuns;
++      __u32   bufferOverRuns;
++      __u32   timeoutErrors;
++      __u32   alignmentErrors;
++};
++
++struct PptpSetLinkInfo {
++      __u16   peersCallID;
++      __u16   reserved;
++      __u32   sendAccm;
++      __u32   recvAccm;
++};
++
++
++struct pptp_priv_data {
++      __u16   call_id;
++      __u16   mcall_id;
++      __u16   pcall_id;
++};
++
++#endif /* __KERNEL__ */
++#endif /* _CONNTRACK_PPTP_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h Wed Sep 24 09:17:55 2003
+@@ -0,0 +1,123 @@
++#ifndef _CONNTRACK_PROTO_GRE_H
++#define _CONNTRACK_PROTO_GRE_H
++#include <asm/byteorder.h>
++
++/* GRE PROTOCOL HEADER */
++
++/* GRE Version field */
++#define GRE_VERSION_1701      0x0
++#define GRE_VERSION_PPTP      0x1
++
++/* GRE Protocol field */
++#define GRE_PROTOCOL_PPTP     0x880B
++
++/* GRE Flags */
++#define GRE_FLAG_C            0x80
++#define GRE_FLAG_R            0x40
++#define GRE_FLAG_K            0x20
++#define GRE_FLAG_S            0x10
++#define GRE_FLAG_A            0x80
++
++#define GRE_IS_C(f)   ((f)&GRE_FLAG_C)
++#define GRE_IS_R(f)   ((f)&GRE_FLAG_R)
++#define GRE_IS_K(f)   ((f)&GRE_FLAG_K)
++#define GRE_IS_S(f)   ((f)&GRE_FLAG_S)
++#define GRE_IS_A(f)   ((f)&GRE_FLAG_A)
++
++/* GRE is a mess: Four different standards */
++struct gre_hdr {
++#if defined(__LITTLE_ENDIAN_BITFIELD)
++      __u16   rec:3,
++              srr:1,
++              seq:1,
++              key:1,
++              routing:1,
++              csum:1,
++              version:3,
++              reserved:4,
++              ack:1;
++#elif defined(__BIG_ENDIAN_BITFIELD)
++      __u16   csum:1,
++              routing:1,
++              key:1,
++              seq:1,
++              srr:1,
++              rec:3,
++              ack:1,
++              reserved:4,
++              version:3;
++#else
++#error "Adjust your <asm/byteorder.h> defines"
++#endif
++      __u16   protocol;
++};
++
++/* modified GRE header for PPTP */
++struct gre_hdr_pptp {
++      __u8  flags;            /* bitfield */
++      __u8  version;          /* should be GRE_VERSION_PPTP */
++      __u16 protocol;         /* should be GRE_PROTOCOL_PPTP */
++      __u16 payload_len;      /* size of ppp payload, not inc. gre header */
++      __u16 call_id;          /* peer's call_id for this session */
++      __u32 seq;              /* sequence number.  Present if S==1 */
++      __u32 ack;              /* seq number of highest packet recieved by */
++                              /*  sender in this session */
++};
++
++
++/* this is part of ip_conntrack */
++struct ip_ct_gre {
++      unsigned int stream_timeout;
++      unsigned int timeout;
++};
++
++/* this is part of ip_conntrack_expect */
++struct ip_ct_gre_expect {
++      struct ip_ct_gre_keymap *keymap_orig, *keymap_reply;
++};
++
++#ifdef __KERNEL__
++struct ip_conntrack_expect;
++
++/* structure for original <-> reply keymap */
++struct ip_ct_gre_keymap {
++      struct list_head list;
++
++      struct ip_conntrack_tuple tuple;
++};
++
++
++/* add new tuple->key_reply pair to keymap */
++int ip_ct_gre_keymap_add(struct ip_conntrack_expect *exp,
++                       struct ip_conntrack_tuple *t,
++                       int reply);
++
++/* change an existing keymap entry */
++void ip_ct_gre_keymap_change(struct ip_ct_gre_keymap *km,
++                           struct ip_conntrack_tuple *t);
++
++/* delete keymap entries */
++void ip_ct_gre_keymap_destroy(struct ip_conntrack_expect *exp);
++
++
++/* get pointer to gre key, if present */
++static inline u_int32_t *gre_key(struct gre_hdr *greh)
++{
++      if (!greh->key)
++              return NULL;
++      if (greh->csum || greh->routing)
++              return (u_int32_t *) (greh+sizeof(*greh)+4);
++      return (u_int32_t *) (greh+sizeof(*greh));
++}
++
++/* get pointer ot gre csum, if present */
++static inline u_int16_t *gre_csum(struct gre_hdr *greh)
++{
++      if (!greh->csum)
++              return NULL;
++      return (u_int16_t *) (greh+sizeof(*greh));
++}
++
++#endif /* __KERNEL__ */
++
++#endif /* _CONNTRACK_PROTO_GRE_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_protocol.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_protocol.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_protocol.h      Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_protocol.h  Wed Sep 24 09:18:12 2003
+@@ -57,6 +57,12 @@
+ extern int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto);
+ extern void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto);
++/* Get the tuple from the packet and return 1 if it's succeeded. */
++extern int
++ip_conntrack_get_tuple(const struct iphdr *iph, size_t len,
++                     struct ip_conntrack_tuple *tuple,
++                     struct ip_conntrack_protocol *protocol);
++
+ /* Existing built-in protocols */
+ extern struct ip_conntrack_protocol ip_conntrack_protocol_tcp;
+ extern struct ip_conntrack_protocol ip_conntrack_protocol_udp;
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_quake3.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_quake3.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_quake3.h        Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_quake3.h    Wed Sep 24 09:17:58 2003
+@@ -0,0 +1,21 @@
++#ifndef _IP_CT_QUAKE3
++#define _IP_CT_QUAKE3
++
++/* Don't confuse with 27960, often used as the Server Port */
++#define QUAKE3_MASTER_PORT 27950
++
++struct quake3_search {
++      const char marker[4]; /* always 0xff 0xff 0xff 0xff ? */
++      const char *pattern;
++      size_t plen;
++}; 
++
++/* This structure is per expected connection */
++struct ip_ct_quake3_expect {
++};
++
++/* This structure exists only once per master */
++struct ip_ct_quake3_master {
++};
++
++#endif /* _IP_CT_QUAKE3 */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_rpc.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_rpc.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_rpc.h   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_rpc.h       Wed Sep 24 09:18:01 2003
+@@ -0,0 +1,68 @@
++/* RPC extension for IP connection tracking, Version 2.2
++ * (C) 2000 by Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>
++ *    - original rpc tracking module
++ *    - "recent" connection handling for kernel 2.3+ netfilter
++ *
++ * (C) 2001 by Rusty Russell <rusty@rustcorp.com.au>
++ *    - upgraded conntrack modules to oldnat api - kernel 2.4.0+
++ *
++ * (C) 2002 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
++ *    - upgraded conntrack modules to newnat api - kernel 2.4.20+
++ *    - extended matching to support filtering on procedures
++ *
++ * ip_conntrack_rpc.h,v 2.2 2003/01/12 18:30:00
++ *
++ *    This program is free software; you can redistribute it and/or
++ *    modify it under the terms of the GNU General Public License
++ *    as published by the Free Software Foundation; either version
++ *    2 of the License, or (at your option) any later version.
++ **
++ */
++
++#include <asm/param.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/stddef.h>
++#include <linux/list.h>
++
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++
++#ifndef _IP_CONNTRACK_RPC_H
++#define _IP_CONNTRACK_RPC_H
++
++#define RPC_PORT       111
++
++
++/* Datum in RPC packets are encoded in XDR */
++#define IXDR_GET_INT32(buf) ((u_int32_t) ntohl((uint32_t)*buf))
++
++/* Fast timeout, to deny DoS atacks */
++#define EXP (60 * HZ)
++
++/* Normal timeouts */
++#define EXPIRES (180 * HZ)
++
++/* For future conections RPC, using client's cache bindings
++ * I'll use ip_conntrack_lock to lock these lists     */
++
++/* This identifies each request and stores protocol */
++struct request_p {
++      struct list_head list;
++
++      u_int32_t xid;   
++      u_int32_t ip;
++      u_int16_t port;
++      
++      /* Protocol */
++      u_int16_t proto;
++
++      struct timer_list timeout;
++};
++
++static inline int request_p_cmp(const struct request_p *p, u_int32_t xid, 
++                              u_int32_t ip, u_int32_t port) {
++      return (p->xid == xid && p->ip == ip && p->port);
++
++}
++
++#endif /* _IP_CONNTRACK_RPC_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_rsh.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_rsh.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_rsh.h   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_rsh.h       Wed Sep 24 09:18:03 2003
+@@ -0,0 +1,35 @@
++/* RSH extension for IP connection tracking, Version 1.0
++ * (C) 2002 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
++ * based on HW's ip_conntrack_irc.c     
++ *
++ * ip_conntrack_rsh.c,v 1.0 2002/07/17 14:49:26
++ *
++ *      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.
++ */
++#ifndef _IP_CONNTRACK_RSH_H
++#define _IP_CONNTRACK_RSH_H
++
++#ifdef __KERNEL__
++#include <linux/netfilter_ipv4/lockhelp.h>
++
++DECLARE_LOCK_EXTERN(ip_rsh_lock);
++#endif
++
++
++#define RSH_PORT      514
++
++/* This structure is per expected connection */
++struct ip_ct_rsh_expect
++{
++      u_int16_t port;
++};
++
++/* This structure exists only once per master */
++struct ip_ct_rsh_master {
++};
++
++#endif /* _IP_CONNTRACK_RSH_H */
++
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_talk.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_talk.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_talk.h  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_talk.h      Wed Sep 24 09:18:08 2003
+@@ -0,0 +1,152 @@
++#ifndef _IP_CONNTRACK_TALK_H
++#define _IP_CONNTRACK_TALK_H
++/* TALK tracking. */
++
++#ifdef __KERNEL__
++#include <linux/in.h>
++#include <linux/netfilter_ipv4/lockhelp.h>
++
++/* Protects talk part of conntracks */
++DECLARE_LOCK_EXTERN(ip_talk_lock);
++#endif
++
++
++#define TALK_PORT     517
++#define NTALK_PORT    518
++
++/* talk structures and constants from <protocols/talkd.h> */
++
++/*
++ * 4.3BSD struct sockaddr
++ */
++struct talk_addr {
++      u_int16_t ta_family;
++      u_int16_t ta_port;
++      u_int32_t ta_addr;
++      u_int32_t ta_junk1;
++      u_int32_t ta_junk2;
++};
++
++#define       TALK_OLD_NSIZE  9
++#define       TALK_NSIZE      12
++#define       TALK_TTY_NSIZE  16
++
++/*
++ * Client->server request message formats.
++ */
++struct talk_msg {
++      u_char  type;           /* request type, see below */
++      char    l_name[TALK_OLD_NSIZE];/* caller's name */
++      char    r_name[TALK_OLD_NSIZE];/* callee's name */
++      u_char  pad;
++      u_int32_t id_num;       /* message id */
++      int32_t pid;            /* caller's process id */
++      char    r_tty[TALK_TTY_NSIZE];/* callee's tty name */
++      struct  talk_addr addr;         /* old (4.3) style */
++      struct  talk_addr ctl_addr;     /* old (4.3) style */
++};
++
++struct ntalk_msg {
++      u_char  vers;           /* protocol version */
++      u_char  type;           /* request type, see below */
++      u_char  answer;         /* not used */
++      u_char  pad;
++      u_int32_t id_num;       /* message id */
++      struct  talk_addr addr;         /* old (4.3) style */
++      struct  talk_addr ctl_addr;     /* old (4.3) style */
++      int32_t pid;            /* caller's process id */
++      char    l_name[TALK_NSIZE];/* caller's name */
++      char    r_name[TALK_NSIZE];/* callee's name */
++      char    r_tty[TALK_TTY_NSIZE];/* callee's tty name */
++};
++
++struct ntalk2_msg {
++      u_char  vers;           /* talk protocol version    */
++      u_char  type;           /* request type             */
++      u_char  answer;         /*  */
++      u_char  extended;       /* !0 if additional parts   */
++      u_int32_t id_num;       /* message id number (dels) */
++      struct  talk_addr addr;         /* target address   */
++      struct  talk_addr ctl_addr;     /* reply to address */
++      int32_t pid;            /* caller's process id */
++      char    l_name[TALK_NSIZE];  /* caller's name */
++      char    r_name[TALK_NSIZE];  /* callee's name */
++      char    r_tty[TALK_TTY_NSIZE];    /* callee's tty */
++};
++
++/*
++ * Server->client response message formats.
++ */
++struct talk_response {
++      u_char  type;           /* type of request message, see below */
++      u_char  answer;         /* response to request message, see below */
++      u_char  pad[2];
++      u_int32_t id_num;       /* message id */
++      struct  talk_addr addr; /* address for establishing conversation */
++};
++
++struct ntalk_response {
++      u_char  vers;           /* protocol version */
++      u_char  type;           /* type of request message, see below */
++      u_char  answer;         /* response to request message, see below */
++      u_char  pad;
++      u_int32_t id_num;       /* message id */
++      struct  talk_addr addr; /* address for establishing conversation */
++};
++
++struct ntalk2_response {
++      u_char  vers;           /* protocol version         */
++      u_char  type;           /* type of request message  */
++      u_char  answer;         /* response to request      */
++      u_char  rvers;          /* Version of answering vers*/
++      u_int32_t id_num;       /* message id number        */
++      struct  talk_addr addr; /* address for connection   */
++      /* This is at the end to compatiblize this with NTALK version.   */
++      char    r_name[TALK_NSIZE]; /* callee's name            */
++};
++
++#define TALK_STR(data, talk_str, member) ((struct talk_str *)data)->member)
++#define TALK_RESP(data, ver, member) (ver ? ((struct ntalk_response *)data)->member : ((struct talk_response *)data)->member)
++#define TALK_MSG(data, ver, member) (ver ? ((struct ntalk_msg *)data)->member : ((struct talk_msg *)data)->member)
++
++#define       TALK_VERSION    0               /* protocol versions */
++#define       NTALK_VERSION   1
++#define       NTALK2_VERSION  2
++
++/* message type values */
++#define LEAVE_INVITE  0       /* leave invitation with server */
++#define LOOK_UP               1       /* check for invitation by callee */
++#define DELETE                2       /* delete invitation by caller */
++#define ANNOUNCE      3       /* announce invitation by caller */
++/* NTALK2 */
++#define REPLY_QUERY   4       /* request reply data from local daemon */
++
++/* answer values */
++#define SUCCESS               0       /* operation completed properly */
++#define NOT_HERE      1       /* callee not logged in */
++#define FAILED                2       /* operation failed for unexplained reason */
++#define MACHINE_UNKNOWN       3       /* caller's machine name unknown */
++#define PERMISSION_DENIED 4   /* callee's tty doesn't permit announce */
++#define UNKNOWN_REQUEST       5       /* request has invalid type value */
++#define       BADVERSION      6       /* request has invalid protocol version */
++#define       BADADDR         7       /* request has invalid addr value */
++#define       BADCTLADDR      8       /* request has invalid ctl_addr value */
++/* NTALK2 */
++#define NO_CALLER     9       /* no-one calling answer from REPLY   */
++#define TRY_HERE      10      /* Not on this machine, try this      */
++#define SELECTIVE_REFUSAL 11  /* User Filter refusal.               */
++#define MAX_RESPONSE_TYPE 11  /* Make sure this is updated          */
++
++/* We don't really need much for talk */
++struct ip_ct_talk_expect
++{
++      /* Port that was to be used */
++      u_int16_t port;
++};
++
++/* This structure exists only once per master */
++struct ip_ct_talk_master
++{
++};
++
++#endif /* _IP_CONNTRACK_TALK_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_tftp.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_tftp.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_tftp.h  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_tftp.h      Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,13 @@
++#ifndef _IP_CT_TFTP
++#define _IP_CT_TFTP
++
++#define TFTP_PORT 69
++
++struct tftphdr {
++      u_int16_t opcode;
++};
++
++#define TFTP_OPCODE_READ      1
++#define TFTP_OPCODE_WRITE     2
++
++#endif /* _IP_CT_TFTP */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h Mon Feb 25 19:38:13 2002
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_conntrack_tuple.h     Wed Sep 24 09:17:55 2003
+@@ -14,7 +14,7 @@
+ union ip_conntrack_manip_proto
+ {
+       /* Add other protocols here. */
+-      u_int16_t all;
++      u_int32_t all;
+       struct {
+               u_int16_t port;
+@@ -25,6 +25,9 @@
+       struct {
+               u_int16_t id;
+       } icmp;
++      struct {
++              u_int32_t key;
++      } gre;
+ };
+ /* The manipulable part of the tuple. */
+@@ -44,7 +47,7 @@
+               u_int32_t ip;
+               union {
+                       /* Add other protocols here. */
+-                      u_int16_t all;
++                      u_int64_t all;
+                       struct {
+                               u_int16_t port;
+@@ -55,6 +58,11 @@
+                       struct {
+                               u_int8_t type, code;
+                       } icmp;
++                      struct {
++                              u_int16_t protocol;
++                              u_int8_t version;
++                              u_int32_t key;
++                      } gre;
+               } u;
+               /* The protocol. */
+@@ -62,6 +70,14 @@
+       } dst;
+ };
++/* This is optimized opposed to a memset of the whole structure.  Everything we
++ * really care about is the  source/destination unions */
++#define IP_CT_TUPLE_BLANK(tuple)                              \
++      do {                                                    \
++              (tuple)->src.u.all = 0;                         \
++              (tuple)->dst.u.all = 0;                         \
++      } while (0)
++
+ enum ip_conntrack_dir
+ {
+       IP_CT_DIR_ORIGINAL,
+@@ -72,10 +88,16 @@
+ #ifdef __KERNEL__
+ #define DUMP_TUPLE(tp)                                                \
+-DEBUGP("tuple %p: %u %u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu\n",   \
++DEBUGP("tuple %p: %u %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n",     \
+        (tp), (tp)->dst.protonum,                              \
+-       NIPQUAD((tp)->src.ip), ntohs((tp)->src.u.all),         \
+-       NIPQUAD((tp)->dst.ip), ntohs((tp)->dst.u.all))
++       NIPQUAD((tp)->src.ip), ntohl((tp)->src.u.all),         \
++       NIPQUAD((tp)->dst.ip), ntohl((tp)->dst.u.all))
++
++#define DUMP_TUPLE_RAW(x)                                             \
++      DEBUGP("tuple %p: %u %u.%u.%u.%u:0x%08x -> %u.%u.%u.%u:0x%08x\n",\
++      (x), (x)->dst.protonum,                                         \
++      NIPQUAD((x)->src.ip), ntohl((x)->src.u.all),                    \
++      NIPQUAD((x)->dst.ip), ntohl((x)->dst.u.all))
+ #define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL)
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_logging.h linux-2.4.20/include/linux/netfilter_ipv4/ip_logging.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_logging.h Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_logging.h     Wed Sep 24 09:16:23 2003
+@@ -0,0 +1,20 @@
++/* IPv4 macros for the internal logging interface. */
++#ifndef __IP_LOGGING_H
++#define __IP_LOGGING_H
++
++#ifdef __KERNEL__
++#include <linux/socket.h>
++#include <linux/netfilter_logging.h>
++
++#define nf_log_ip_packet(pskb,hooknum,in,out,fmt,args...) \
++      nf_log_packet(AF_INET,pskb,hooknum,in,out,fmt,##args)
++
++#define nf_log_ip(pfh,len,fmt,args...) \
++      nf_log(AF_INET,pfh,len,fmt,##args)
++
++#define nf_ip_log_register(logging) nf_log_register(AF_INET,logging)
++#define nf_ip_log_unregister(logging) nf_log_unregister(AF_INET,logging)
++      
++#endif /*__KERNEL__*/
++
++#endif /*__IP_LOGGING_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_nat_helper.h linux-2.4.20/include/linux/netfilter_ipv4/ip_nat_helper.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_nat_helper.h      Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_nat_helper.h  Wed Sep 24 09:16:14 2003
+@@ -50,6 +50,13 @@
+                               unsigned int match_len,
+                               char *rep_buffer,
+                               unsigned int rep_len);
++extern int ip_nat_mangle_udp_packet(struct sk_buff **skb,
++                              struct ip_conntrack *ct,
++                              enum ip_conntrack_info ctinfo,
++                              unsigned int match_offset,
++                              unsigned int match_len,
++                              char *rep_buffer,
++                              unsigned int rep_len);
+ extern int ip_nat_seq_adjust(struct sk_buff *skb,
+                               struct ip_conntrack *ct,
+                               enum ip_conntrack_info ctinfo);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_nat_pptp.h linux-2.4.20/include/linux/netfilter_ipv4/ip_nat_pptp.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_nat_pptp.h        Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_nat_pptp.h    Wed Sep 24 09:17:55 2003
+@@ -0,0 +1,11 @@
++/* PPTP constants and structs */
++#ifndef _NAT_PPTP_H
++#define _NAT_PPTP_H
++
++/* conntrack private data */
++struct ip_nat_pptp {
++      u_int16_t pns_call_id;          /* NAT'ed PNS call id */
++      u_int16_t pac_call_id;          /* NAT'ed PAC call id */
++};
++
++#endif /* _NAT_PPTP_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_nat_rule.h linux-2.4.20/include/linux/netfilter_ipv4/ip_nat_rule.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_nat_rule.h        Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_nat_rule.h    Wed Sep 24 09:16:27 2003
+@@ -14,5 +14,10 @@
+                           const struct net_device *out,
+                           struct ip_conntrack *ct,
+                           struct ip_nat_info *info);
++
++extern unsigned int
++alloc_null_binding(struct ip_conntrack *conntrack,
++                 struct ip_nat_info *info,
++                 unsigned int hooknum);
+ #endif
+ #endif /* _IP_NAT_RULE_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ip_pool.h linux-2.4.20/include/linux/netfilter_ipv4/ip_pool.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ip_pool.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ip_pool.h        Wed Sep 24 09:16:59 2003
+@@ -0,0 +1,64 @@
++#ifndef _IP_POOL_H
++#define _IP_POOL_H
++
++/***************************************************************************/
++/*  This program is free software; you can redistribute it and/or modify   */
++/*  it under the terms of the GNU General Public License as published by   */
++/*  the Free Software Foundation; either version 2 of the License, or    */
++/*  (at your option) any later version.                                          */
++/*                                                                       */
++/*  This program is distributed in the hope that it will be useful,      */
++/*  but WITHOUT ANY WARRANTY; without even the implied warranty of       */
++/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
++/*  GNU General Public License for more details.                         */
++/*                                                                       */
++/*  You should have received a copy of the GNU General Public License    */
++/*  along with this program; if not, write to the Free Software                  */
++/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/
++/***************************************************************************/
++
++/* A sockopt of such quality has hardly ever been seen before on the open
++ * market!  This little beauty, hardly ever used: above 64, so it's
++ * traditionally used for firewalling, not touched (even once!) by the
++ * 2.0, 2.2 and 2.4 kernels!
++ *
++ * Comes with its own certificate of authenticity, valid anywhere in the
++ * Free world!
++ *
++ * Rusty, 19.4.2000
++ */
++#define SO_IP_POOL 81
++
++typedef int ip_pool_t;                        /* pool index */
++#define IP_POOL_NONE  ((ip_pool_t)-1)
++
++struct ip_pool_request {
++      int op;
++      ip_pool_t index;
++      u_int32_t addr;
++      u_int32_t addr2;
++};
++
++/* NOTE: I deliberately break the first cut ippool utility. Nobody uses it. */
++
++#define IP_POOL_BAD001                0x00000010
++
++#define IP_POOL_FLUSH         0x00000011      /* req.index, no arguments */
++#define IP_POOL_INIT          0x00000012      /* from addr to addr2 incl. */
++#define IP_POOL_DESTROY               0x00000013      /* req.index, no arguments */
++#define IP_POOL_ADD_ADDR      0x00000014      /* add addr to pool */
++#define IP_POOL_DEL_ADDR      0x00000015      /* del addr from pool */
++#define IP_POOL_HIGH_NR               0x00000016      /* result in req.index */
++#define IP_POOL_LOOKUP                0x00000017      /* result in addr and addr2 */
++#define IP_POOL_USAGE         0x00000018      /* result in addr */
++#define IP_POOL_TEST_ADDR     0x00000019      /* result (0/1) returned */
++
++#ifdef __KERNEL__
++
++/* NOTE: ip_pool_match() and ip_pool_mod() expect ADDR to be host byte order */
++extern int ip_pool_match(ip_pool_t pool, u_int32_t addr);
++extern int ip_pool_mod(ip_pool_t pool, u_int32_t addr, int isdel);
++
++#endif
++
++#endif /*_IP_POOL_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_CLASSIFY.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_CLASSIFY.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_CLASSIFY.h       Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_CLASSIFY.h   Wed Sep 24 09:17:14 2003
+@@ -0,0 +1,8 @@
++#ifndef _IPT_CLASSIFY_H
++#define _IPT_CLASSIFY_H
++
++struct ipt_classify_target_info {
++      u_int32_t priority;
++};
++
++#endif /*_IPT_CLASSIFY_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_CONNMARK.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_CONNMARK.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_CONNMARK.h       Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_CONNMARK.h   Wed Sep 24 09:17:17 2003
+@@ -0,0 +1,15 @@
++#ifndef _IPT_CONNMARK_H_target
++#define _IPT_CONNMARK_H_target
++
++enum {
++    IPT_CONNMARK_SET = 0,
++    IPT_CONNMARK_SAVE,
++    IPT_CONNMARK_RESTORE
++};
++
++struct ipt_connmark_target_info {
++      unsigned long mark;
++      u_int8_t mode;
++};
++
++#endif /*_IPT_CONNMARK_H_target*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_IMQ.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_IMQ.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_IMQ.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_IMQ.h        Wed Sep 24 09:17:19 2003
+@@ -0,0 +1,8 @@
++#ifndef _IPT_IMQ_H
++#define _IPT_IMQ_H
++
++struct ipt_imq_info {
++      unsigned int todev;     /* target imq device */
++};
++
++#endif /* _IPT_IMQ_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_IPMARK.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_IPMARK.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_IPMARK.h Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_IPMARK.h     Wed Sep 24 09:17:23 2003
+@@ -0,0 +1,13 @@
++#ifndef _IPT_IPMARK_H_target
++#define _IPT_IPMARK_H_target
++
++struct ipt_ipmark_target_info {
++      unsigned long andmask;
++      unsigned long ormask;
++      unsigned int addr;
++};
++
++#define IPT_IPMARK_SRC    0
++#define IPT_IPMARK_DST    1
++
++#endif /*_IPT_IPMARK_H_target*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_NETLINK.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_NETLINK.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_NETLINK.h        Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_NETLINK.h    Wed Sep 24 09:16:32 2003
+@@ -0,0 +1,27 @@
++#ifndef _IPT_FWMON_H
++#define _IPT_FWMON_H
++
++/* Bitmask macros */
++#define MASK(x,y) (x & y)
++#define MASK_SET(x,y) x |= y
++#define MASK_UNSET(x,y) x &= ~y
++
++#define USE_MARK      0x00000001
++#define USE_DROP      0x00000002
++#define USE_SIZE      0x00000004
++
++struct ipt_nldata
++{     
++      unsigned int flags;
++      unsigned int mark;
++      unsigned int size;
++};
++
++/* Old header */
++struct netlink_t {
++      unsigned int len;
++      unsigned int mark;
++      char iface[IFNAMSIZ];
++};
++
++#endif /*_IPT_FWMON_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_REJECT.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_REJECT.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_REJECT.h Fri Jul 14 19:20:23 2000
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_REJECT.h     Wed Sep 24 09:18:09 2003
+@@ -9,11 +9,13 @@
+       IPT_ICMP_ECHOREPLY,
+       IPT_ICMP_NET_PROHIBITED,
+       IPT_ICMP_HOST_PROHIBITED,
+-      IPT_TCP_RESET
++      IPT_TCP_RESET,
++      IPT_ICMP_ADMIN_PROHIBITED
+ };
+ struct ipt_reject_info {
+       enum ipt_reject_with with;      /* reject type */
++      u_int8_t fake_source_address;  /* 1: fake src addr with original packet dest, 0: no fake */
+ };
+-#endif /*_IPT_REJECT_H*/
++#endif /* _IPT_REJECT_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_ROUTE.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_ROUTE.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_ROUTE.h  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_ROUTE.h      Wed Sep 24 09:17:25 2003
+@@ -0,0 +1,22 @@
++/* Header file for iptables ipt_ROUTE target
++ *
++ * (C) 2002 by Cédric de Launois <delaunois@info.ucl.ac.be>
++ *
++ * This software is distributed under GNU GPL v2, 1991
++ */
++#ifndef _IPT_ROUTE_H_target
++#define _IPT_ROUTE_H_target
++
++#define IPT_ROUTE_IFNAMSIZ 16
++
++struct ipt_route_target_info {
++      char      oif[IPT_ROUTE_IFNAMSIZ];      /* Output Interface Name */
++      char      iif[IPT_ROUTE_IFNAMSIZ];      /* Input Interface Name  */
++      u_int32_t gw;                           /* IP address of gateway */
++      u_int8_t  flags;
++};
++
++/* Values for "flags" field */
++#define IPT_ROUTE_CONTINUE        0x01
++
++#endif /*_IPT_ROUTE_H_target*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_SAME.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_SAME.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_SAME.h   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_SAME.h       Wed Sep 24 09:16:38 2003
+@@ -0,0 +1,19 @@
++#ifndef _IPT_SAME_H
++#define _IPT_SAME_H
++
++#define IPT_SAME_MAX_RANGE    10
++
++#define IPT_SAME_NODST                0x01
++
++struct ipt_same_info
++{
++      unsigned char info;
++      u_int32_t rangesize;
++      u_int32_t ipnum;
++      u_int32_t *iparray;
++
++      /* hangs off end. */
++      struct ip_nat_range range[IPT_SAME_MAX_RANGE];
++};
++
++#endif /*_IPT_SAME_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_TCPLAG.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_TCPLAG.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_TCPLAG.h Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_TCPLAG.h     Wed Sep 24 09:17:29 2003
+@@ -0,0 +1,10 @@
++#ifndef _IPT_TCPLAG_H
++#define _IPT_TCPLAG_H
++
++struct ipt_tcplag
++{
++      unsigned char level;
++      unsigned char prefix[ 15 ];
++};
++
++#endif
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_TTL.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_TTL.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_TTL.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_TTL.h        Wed Sep 24 09:16:40 2003
+@@ -0,0 +1,21 @@
++/* TTL modification module for IP tables
++ * (C) 2000 by Harald Welte <laforge@gnumonks.org> */
++
++#ifndef _IPT_TTL_H
++#define _IPT_TTL_H
++
++enum {
++      IPT_TTL_SET = 0,
++      IPT_TTL_INC,
++      IPT_TTL_DEC
++};
++
++#define IPT_TTL_MAXMODE       IPT_TTL_DEC
++
++struct ipt_TTL_info {
++      u_int8_t        mode;
++      u_int8_t        ttl;
++};
++
++
++#endif
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_ULOG.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_ULOG.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_ULOG.h   Mon Feb 25 19:38:13 2002
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_ULOG.h       Wed Sep 24 09:16:23 2003
+@@ -11,6 +11,9 @@
+ #define NETLINK_NFLOG         5
+ #endif
++#define ULOG_DEFAULT_NLGROUP  1
++#define ULOG_DEFAULT_QTHRESHOLD       1
++
+ #define ULOG_MAC_LEN  80
+ #define ULOG_PREFIX_LEN       32
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_XOR.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_XOR.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_XOR.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_XOR.h        Wed Sep 24 09:17:31 2003
+@@ -0,0 +1,9 @@
++#ifndef _IPT_XOR_H
++#define _IPT_XOR_H
++
++struct ipt_XOR_info {
++      char            key[30];
++      u_int8_t        block_size;
++};
++
++#endif /* _IPT_XOR_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_addrtype.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_addrtype.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_addrtype.h       Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_addrtype.h   Wed Sep 24 09:17:32 2003
+@@ -0,0 +1,11 @@
++#ifndef _IPT_ADDRTYPE_H
++#define _IPT_ADDRTYPE_H
++
++struct ipt_addrtype_info {
++      u_int16_t       source;         /* source-type mask */
++      u_int16_t       dest;           /* dest-type mask */
++      int             invert_source;
++      int             invert_dest;
++};
++
++#endif
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_condition.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_condition.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_condition.h      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_condition.h  Wed Sep 24 09:17:34 2003
+@@ -0,0 +1,11 @@
++#ifndef __IPT_CONDITION_MATCH__
++#define __IPT_CONDITION_MATCH__
++
++#define CONDITION_NAME_LEN  32
++
++struct condition_info {
++      char name[CONDITION_NAME_LEN];
++      int  invert;
++};
++
++#endif
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_connlimit.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_connlimit.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_connlimit.h      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_connlimit.h  Wed Sep 24 09:16:42 2003
+@@ -0,0 +1,12 @@
++#ifndef _IPT_CONNLIMIT_H
++#define _IPT_CONNLIMIT_H
++
++struct ipt_connlimit_data;
++
++struct ipt_connlimit_info {
++      int limit;
++      int inverse;
++      u_int32_t mask;
++      struct ipt_connlimit_data *data;
++};
++#endif /* _IPT_CONNLIMIT_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_connmark.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_connmark.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_connmark.h       Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_connmark.h   Wed Sep 24 09:17:17 2003
+@@ -0,0 +1,9 @@
++#ifndef _IPT_CONNMARK_H
++#define _IPT_CONNMARK_H
++
++struct ipt_connmark_info {
++      unsigned long mark, mask;
++      u_int8_t invert;
++};
++
++#endif /*_IPT_CONNMARK_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_conntrack.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_conntrack.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_conntrack.h      Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_conntrack.h  Wed Sep 24 09:18:12 2003
+@@ -11,6 +11,8 @@
+ #define IPT_CONNTRACK_STATE_SNAT (1 << (IP_CT_NUMBER + 1))
+ #define IPT_CONNTRACK_STATE_DNAT (1 << (IP_CT_NUMBER + 2))
++#define IPT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_IS_REPLY+3))
++
+ /* flags, invflags: */
+ #define IPT_CONNTRACK_STATE   0x01
+ #define IPT_CONNTRACK_PROTO   0x02
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_fuzzy.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_fuzzy.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_fuzzy.h  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_fuzzy.h      Wed Sep 24 09:16:44 2003
+@@ -0,0 +1,21 @@
++#ifndef _IPT_FUZZY_H
++#define _IPT_FUZZY_H
++
++#include <linux/param.h>
++#include <linux/types.h>
++
++#define MAXFUZZYRATE 10000000
++#define MINFUZZYRATE 3
++
++struct ipt_fuzzy_info {
++      u_int32_t minimum_rate;
++      u_int32_t maximum_rate;
++      u_int32_t packets_total;
++      u_int32_t bytes_total;
++      u_int32_t previous_time;
++      u_int32_t present_time;
++      u_int32_t mean_rate;
++      u_int8_t acceptance_rate;
++};
++
++#endif /*_IPT_FUZZY_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_iprange.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_iprange.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_iprange.h        Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_iprange.h    Wed Sep 24 09:16:47 2003
+@@ -0,0 +1,23 @@
++#ifndef _IPT_IPRANGE_H
++#define _IPT_IPRANGE_H
++
++#define IPRANGE_SRC           0x01    /* Match source IP address */
++#define IPRANGE_DST           0x02    /* Match destination IP address */
++#define IPRANGE_SRC_INV               0x10    /* Negate the condition */
++#define IPRANGE_DST_INV               0x20    /* Negate the condition */
++
++struct ipt_iprange {
++      /* Inclusive: network order. */
++      u_int32_t min_ip, max_ip;
++};
++
++struct ipt_iprange_info
++{
++      struct ipt_iprange src;
++      struct ipt_iprange dst;
++
++      /* Flags from above */
++      u_int8_t flags;
++};
++
++#endif /* _IPT_IPRANGE_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_ipv4options.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_ipv4options.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_ipv4options.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_ipv4options.h        Wed Sep 24 09:16:49 2003
+@@ -0,0 +1,21 @@
++#ifndef __ipt_ipv4options_h_included__
++#define __ipt_ipv4options_h_included__
++
++#define IPT_IPV4OPTION_MATCH_SSRR             0x01  /* For strict source routing */
++#define IPT_IPV4OPTION_MATCH_LSRR             0x02  /* For loose source routing */
++#define IPT_IPV4OPTION_DONT_MATCH_SRR         0x04  /* any source routing */
++#define IPT_IPV4OPTION_MATCH_RR                       0x08  /* For Record route */
++#define IPT_IPV4OPTION_DONT_MATCH_RR          0x10
++#define IPT_IPV4OPTION_MATCH_TIMESTAMP                0x20  /* For timestamp request */
++#define IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP   0x40
++#define IPT_IPV4OPTION_MATCH_ROUTER_ALERT     0x80  /* For router-alert */
++#define IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT        0x100
++#define IPT_IPV4OPTION_MATCH_ANY_OPT          0x200 /* match packet with any option */
++#define IPT_IPV4OPTION_DONT_MATCH_ANY_OPT     0x400 /* match packet with no option */
++
++struct ipt_ipv4options_info {
++      u_int16_t options;
++};
++
++
++#endif /* __ipt_ipv4options_h_included__ */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_mark.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_mark.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_mark.h   Fri Mar 17 18:56:20 2000
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_mark.h       Wed Sep 24 09:18:10 2003
+@@ -1,9 +1,16 @@
+ #ifndef _IPT_MARK_H
+ #define _IPT_MARK_H
++enum {
++        IPT_MARK_BIT_OP_NONE,
++        IPT_MARK_BIT_OP_AND,
++        IPT_MARK_BIT_OP_OR
++};
++
+ struct ipt_mark_info {
+     unsigned long mark, mask;
+     u_int8_t invert;
++    u_int8_t bit_op;
+ };
+ #endif /*_IPT_MARK_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_mport.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_mport.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_mport.h  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_mport.h      Wed Sep 24 09:16:51 2003
+@@ -0,0 +1,24 @@
++#ifndef _IPT_MPORT_H
++#define _IPT_MPORT_H
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++#define IPT_MPORT_SOURCE (1<<0)
++#define IPT_MPORT_DESTINATION (1<<1)
++#define IPT_MPORT_EITHER (IPT_MPORT_SOURCE|IPT_MPORT_DESTINATION)
++
++#define IPT_MULTI_PORTS       15
++
++/* Must fit inside union ipt_matchinfo: 32 bytes */
++/* every entry in ports[] except for the last one has one bit in pflags
++ * associated with it. If this bit is set, the port is the first port of
++ * a portrange, with the next entry being the last.
++ * End of list is marked with pflags bit set and port=65535.
++ * If 14 ports are used (last one does not have a pflag), the last port
++ * is repeated to fill the last entry in ports[] */
++struct ipt_mport
++{
++      u_int8_t flags:2;                       /* Type of comparison */
++      u_int16_t pflags:14;                    /* Port flags */
++      u_int16_t ports[IPT_MULTI_PORTS];       /* Ports */
++};
++#endif /*_IPT_MPORT_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_nth.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_nth.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_nth.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_nth.h        Wed Sep 24 09:16:53 2003
+@@ -0,0 +1,19 @@
++#ifndef _IPT_NTH_H
++#define _IPT_NTH_H
++
++#include <linux/param.h>
++#include <linux/types.h>
++
++#ifndef IPT_NTH_NUM_COUNTERS
++#define IPT_NTH_NUM_COUNTERS 16
++#endif
++
++struct ipt_nth_info {
++      u_int8_t every;
++      u_int8_t not;
++      u_int8_t startat;
++      u_int8_t counter;
++      u_int8_t packet;
++};
++
++#endif /*_IPT_NTH_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_osf.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_osf.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_osf.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_osf.h        Wed Sep 24 09:16:57 2003
+@@ -0,0 +1,120 @@
++/*
++ * ipt_osf.h
++ *
++ * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#ifndef _IPT_OSF_H
++#define _IPT_OSF_H
++
++#define MAXGENRELEN           32
++#define MAXDETLEN             64
++
++#include <linux/list.h>
++
++struct ipt_osf_info
++{
++      char                    genre[MAXGENRELEN];
++      int                     len;
++      int                     invert; /* UNSUPPORTED */
++};
++
++struct osf_wc
++{
++      char                    wc;
++      unsigned long           val;
++};
++
++/* This struct represents IANA options
++ * http://www.iana.org/assignments/tcp-parameters
++ */
++struct osf_opt
++{
++      unsigned char           kind;
++      unsigned char           length;
++      struct osf_wc           wc;
++};
++
++#ifdef __KERNEL__
++
++struct osf_finger
++{
++      struct list_head        flist;
++      struct osf_wc           wss;
++      unsigned char           ttl;
++      unsigned char           df;
++      unsigned long           ss;
++      char                    genre[MAXGENRELEN];
++      
++      /* Not needed, but for consistency with original table from Michal Zalewski */
++      char                    details[MAXDETLEN]; 
++
++      int                     opt_num;
++      struct osf_opt          opt[MAX_IPOPTLEN]; /* In case it is all NOP or EOL */
++
++};
++
++/* Defines for IANA option kinds */
++
++#define OSFOPT_EOL            0       /* End of options */
++#define OSFOPT_NOP            1       /* NOP */
++#define OSFOPT_MSS            2       /* Maximum segment size */
++#define OSFOPT_WSO            3       /* Window scale option */
++#define OSFOPT_SACKP          4       /* SACK permitted */
++#define OSFOPT_SACK           5       /* SACK */
++#define OSFOPT_ECHO           6       
++#define OSFOPT_ECHOREPLY      7
++#define OSFOPT_TS             8       /* Timestamp option */
++#define OSFOPT_POCP           9       /* Partial Order Connection Permitted */
++#define OSFOPT_POSP           10      /* Partial Order Service Profile */
++/* Others are not used in current OSF */
++
++static struct osf_opt IANA_opts[] = 
++{
++      {0, 1,},
++      {1, 1,},
++      {2, 4,},
++      {3, 3,},
++      {4, 2,},
++      {5, 1 ,}, /* SACK length is not defined */
++      {6, 6,},
++      {7, 6,},
++      {8, 10,},
++      {9, 2,},
++      {10, 3,},
++      {11, 1,}, /* CC: Suppose 1 */
++      {12, 1,}, /* the same */
++      {13, 1,}, /* and here too */
++      {14, 3,},
++      {15, 1,}, /* TCP Alternate Checksum Data. Length is not defined */
++      {16, 1,},
++      {17, 1,},
++      {18, 3,},
++      {19, 18,},
++      {20, 1,},
++      {21, 1,},
++      {22, 1,},
++      {23, 1,},
++      {24, 1,},
++      {25, 1,},
++      {26, 1,},
++};
++
++#endif /* __KERNEL__ */
++
++#endif /* _IPT_OSF_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_pool.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_pool.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_pool.h   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_pool.h       Wed Sep 24 09:16:59 2003
+@@ -0,0 +1,25 @@
++#ifndef _IPT_POOL_H
++#define _IPT_POOL_H
++
++#include <linux/netfilter_ipv4/ip_pool.h>
++
++#define IPT_POOL_INV_SRC      0x00000001
++#define IPT_POOL_INV_DST      0x00000002
++#define IPT_POOL_DEL_SRC      0x00000004
++#define IPT_POOL_DEL_DST      0x00000008
++#define IPT_POOL_INV_MOD_SRC  0x00000010
++#define IPT_POOL_INV_MOD_DST  0x00000020
++#define IPT_POOL_MOD_SRC_ACCEPT       0x00000040
++#define IPT_POOL_MOD_DST_ACCEPT       0x00000080
++#define IPT_POOL_MOD_SRC_DROP 0x00000100
++#define IPT_POOL_MOD_DST_DROP 0x00000200
++
++/* match info */
++struct ipt_pool_info
++{
++      ip_pool_t src;
++      ip_pool_t dst;
++      unsigned flags;
++};
++
++#endif /*_IPT_POOL_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_psd.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_psd.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_psd.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_psd.h        Wed Sep 24 09:17:01 2003
+@@ -0,0 +1,40 @@
++#ifndef _IPT_PSD_H
++#define _IPT_PSD_H
++
++#include <linux/param.h>
++#include <linux/types.h>
++
++/*
++ * High port numbers have a lower weight to reduce the frequency of false
++ * positives, such as from passive mode FTP transfers.
++ */
++#define PORT_WEIGHT_PRIV              3
++#define PORT_WEIGHT_HIGH              1
++
++/*
++ * Port scan detection thresholds: at least COUNT ports need to be scanned
++ * from the same source, with no longer than DELAY ticks between ports.
++ */
++#define SCAN_MIN_COUNT                        7
++#define SCAN_MAX_COUNT                        (SCAN_MIN_COUNT * PORT_WEIGHT_PRIV)
++#define SCAN_WEIGHT_THRESHOLD         SCAN_MAX_COUNT
++#define SCAN_DELAY_THRESHOLD          (HZ * 3)
++
++/*
++ * Keep track of up to LIST_SIZE source addresses, using a hash table of
++ * HASH_SIZE entries for faster lookups, but limiting hash collisions to
++ * HASH_MAX source addresses per the same hash value.
++ */
++#define LIST_SIZE                     0x100
++#define HASH_LOG                      9
++#define HASH_SIZE                     (1 << HASH_LOG)
++#define HASH_MAX                      0x10
++
++struct ipt_psd_info {
++      unsigned int weight_threshold;
++      unsigned int delay_threshold;
++      unsigned short lo_ports_weight;
++      unsigned short hi_ports_weight;
++};
++
++#endif /*_IPT_PSD_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_quota.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_quota.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_quota.h  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_quota.h      Wed Sep 24 09:17:03 2003
+@@ -0,0 +1,11 @@
++#ifndef _IPT_QUOTA_H
++#define _IPT_QUOTA_H
++
++/* print debug info in both kernel/netfilter module & iptable library */
++//#define DEBUG_IPT_QUOTA
++
++struct ipt_quota_info {
++        u_int64_t quota;
++};
++
++#endif /*_IPT_QUOTA_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_random.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_random.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_random.h Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_random.h     Wed Sep 24 09:17:05 2003
+@@ -0,0 +1,11 @@
++#ifndef _IPT_RAND_H
++#define _IPT_RAND_H
++
++#include <linux/param.h>
++#include <linux/types.h>
++
++struct ipt_rand_info {
++      u_int8_t average;
++};
++
++#endif /*_IPT_RAND_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_realm.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_realm.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_realm.h  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_realm.h      Wed Sep 24 09:17:09 2003
+@@ -0,0 +1,9 @@
++#ifndef _IPT_REALM_H
++#define _IPT_REALM_H
++
++struct ipt_realm_info {
++      u_int32_t id;
++      u_int32_t mask;
++      u_int8_t invert;
++};
++#endif /*_IPT_REALM_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_recent.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_recent.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_recent.h Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_recent.h     Wed Sep 24 09:16:17 2003
+@@ -0,0 +1,27 @@
++#ifndef _IPT_RECENT_H
++#define _IPT_RECENT_H
++
++#define RECENT_NAME   "ipt_recent"
++#define RECENT_VER    "v0.3.1"
++
++#define IPT_RECENT_CHECK  1
++#define IPT_RECENT_SET    2
++#define IPT_RECENT_UPDATE 4
++#define IPT_RECENT_REMOVE 8
++#define IPT_RECENT_TTL   16
++
++#define IPT_RECENT_SOURCE 0
++#define IPT_RECENT_DEST   1
++
++#define IPT_RECENT_NAME_LEN 200
++
++struct ipt_recent_info {
++      u_int32_t   seconds;
++      u_int32_t   hit_count;
++      u_int8_t    check_set;
++      u_int8_t    invert;
++      char        name[IPT_RECENT_NAME_LEN];
++      u_int8_t    side;
++};
++
++#endif /*_IPT_RECENT_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_rpc.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_rpc.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_rpc.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_rpc.h        Wed Sep 24 09:18:01 2003
+@@ -0,0 +1,35 @@
++/* RPC extension for IP netfilter matching, Version 2.2
++ * (C) 2000 by Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>
++ *    - original rpc tracking module
++ *    - "recent" connection handling for kernel 2.3+ netfilter
++ *
++ * (C) 2001 by Rusty Russell <rusty@rustcorp.com.au>
++ *    - upgraded conntrack modules to oldnat api - kernel 2.4.0+
++ *
++ * (C) 2002 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
++ *    - upgraded conntrack modules to newnat api - kernel 2.4.20+
++ *    - extended matching to support filtering on procedures
++ *
++ * ipt_rpc.h.c,v 2.2 2003/01/12 18:30:00
++ *
++ *    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.
++ **
++ */
++
++#ifndef _IPT_RPC_H
++#define _IPT_RPC_H
++
++struct ipt_rpc_data;
++
++struct ipt_rpc_info {
++      int inverse;
++      int strict;
++      const char c_procs[1408];
++      int i_procs;
++      struct ipt_rpc_data *data;
++};
++
++#endif /* _IPT_RPC_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_state.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_state.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_state.h  Fri Apr 14 16:37:20 2000
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_state.h      Wed Sep 24 09:18:12 2003
+@@ -3,6 +3,7 @@
+ #define IPT_STATE_BIT(ctinfo) (1 << ((ctinfo)%IP_CT_IS_REPLY+1))
+ #define IPT_STATE_INVALID (1 << 0)
++#define IPT_STATE_UNTRACKED (1 << (IP_CT_IS_REPLY+1))
+ struct ipt_state_info
+ {
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_string.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_string.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_string.h Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_string.h     Wed Sep 24 09:18:05 2003
+@@ -0,0 +1,21 @@
++#ifndef _IPT_STRING_H
++#define _IPT_STRING_H
++
++/* *** PERFORMANCE TWEAK ***
++ * Packet size and search string threshold,
++ * above which sublinear searches is used. */
++#define IPT_STRING_HAYSTACK_THRESH    100
++#define IPT_STRING_NEEDLE_THRESH      20
++
++#define BM_MAX_NLEN 256
++#define BM_MAX_HLEN 1024
++
++typedef char *(*proc_ipt_search) (char *, char *, int, int);
++
++struct ipt_string_info {
++    char string[BM_MAX_NLEN];
++    u_int16_t invert;
++    u_int16_t len;
++};
++
++#endif /* _IPT_STRING_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_time.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_time.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_time.h   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_time.h       Wed Sep 24 09:17:10 2003
+@@ -0,0 +1,13 @@
++#ifndef __ipt_time_h_included__
++#define __ipt_time_h_included__
++
++
++struct ipt_time_info {
++      u_int8_t  days_match;   /* 1 bit per day. -SMTWTFS                      */
++      u_int16_t time_start;   /* 0 < time_start < 23*60+59 = 1439             */
++      u_int16_t time_stop;    /* 0:0 < time_stat < 23:59                      */
++      u_int8_t  kerneltime;   /* ignore skb time (and use kerneltime) or not. */
++};
++
++
++#endif /* __ipt_time_h_included__ */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_u32.h linux-2.4.20/include/linux/netfilter_ipv4/ipt_u32.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/ipt_u32.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_u32.h        Wed Sep 24 09:17:12 2003
+@@ -0,0 +1,40 @@
++#ifndef _IPT_U32_H
++#define _IPT_U32_H
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++enum ipt_u32_ops
++{
++      IPT_U32_AND,
++      IPT_U32_LEFTSH,
++      IPT_U32_RIGHTSH,
++      IPT_U32_AT
++};
++
++struct ipt_u32_location_element
++{
++      u_int32_t number;
++      u_int8_t nextop;
++};
++struct ipt_u32_value_element
++{
++      u_int32_t min;
++      u_int32_t max;
++};
++/* *** any way to allow for an arbitrary number of elements?
++   for now I settle for a limit of 10 of each */
++#define U32MAXSIZE 10
++struct ipt_u32_test
++{
++      u_int8_t nnums;
++      struct ipt_u32_location_element location[U32MAXSIZE+1];
++      u_int8_t nvalues;
++      struct ipt_u32_value_element value[U32MAXSIZE+1];
++};
++
++struct ipt_u32
++{
++      u_int8_t ntests;
++      struct ipt_u32_test tests[U32MAXSIZE+1];
++};
++
++#endif /*_IPT_U32_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/listhelp.h linux-2.4.20/include/linux/netfilter_ipv4/listhelp.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/listhelp.h   Thu Jul 26 20:52:12 2001
++++ linux-2.4.20/include/linux/netfilter_ipv4/listhelp.h       Wed Sep 24 09:16:17 2003
+@@ -39,6 +39,22 @@
+       (type)__i;                              \
+ })
++/* Just like LIST_FIND but we search backwards */
++#define LIST_FIND_B(head, cmpfn, type, args...)               \
++({                                                    \
++      const struct list_head *__i = (head);           \
++                                                      \
++      ASSERT_READ_LOCK(head);                         \
++      do {                                            \
++              __i = __i->prev;                        \
++              if (__i == (head)) {                    \
++                      __i = NULL;                     \
++                      break;                          \
++              }                                       \
++      } while (!cmpfn((const type)__i , ## args));    \
++      (type)__i;                                      \
++})
++
+ static inline int
+ __list_cmp_same(const void *p1, const void *p2) { return p1 == p2; }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv4/lockhelp.h linux-2.4.20/include/linux/netfilter_ipv4/lockhelp.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv4/lockhelp.h   Mon Jan  1 18:37:41 2001
++++ linux-2.4.20/include/linux/netfilter_ipv4/lockhelp.h       Wed Sep 24 09:16:17 2003
+@@ -42,22 +42,22 @@
+       printk("ASSERT %s:%u %s locked\n", __FILE__, __LINE__, #l);     \
+ } while(0)
+-/* Write locked OK as well. */                                                    \
++/* Write locked OK as well. */
+ #define MUST_BE_READ_LOCKED(l)                                                    \
+-do { if (!((l)->read_locked_map & (1 << smp_processor_id()))              \
+-       && !((l)->write_locked_map & (1 << smp_processor_id())))           \
++do { if (!((l)->read_locked_map & (1UL << smp_processor_id()))                    \
++       && !((l)->write_locked_map & (1UL << smp_processor_id())))         \
+       printk("ASSERT %s:%u %s not readlocked\n", __FILE__, __LINE__, #l); \
+ } while(0)
+ #define MUST_BE_WRITE_LOCKED(l)                                                    \
+-do { if (!((l)->write_locked_map & (1 << smp_processor_id())))                     \
++do { if (!((l)->write_locked_map & (1UL << smp_processor_id())))           \
+       printk("ASSERT %s:%u %s not writelocked\n", __FILE__, __LINE__, #l); \
+ } while(0)
+ #define MUST_BE_READ_WRITE_UNLOCKED(l)                                          \
+-do { if ((l)->read_locked_map & (1 << smp_processor_id()))              \
++do { if ((l)->read_locked_map & (1UL << smp_processor_id()))            \
+       printk("ASSERT %s:%u %s readlocked\n", __FILE__, __LINE__, #l);   \
+- else if ((l)->write_locked_map & (1 << smp_processor_id()))            \
++ else if ((l)->write_locked_map & (1UL << smp_processor_id()))                  \
+        printk("ASSERT %s:%u %s writelocked\n", __FILE__, __LINE__, #l); \
+ } while(0)
+@@ -91,7 +91,7 @@
+ #define READ_UNLOCK(lk)                                                       \
+ do {                                                                  \
+-      if (!((lk)->read_locked_map & (1 << smp_processor_id())))       \
++      if (!((lk)->read_locked_map & (1UL << smp_processor_id())))     \
+               printk("ASSERT: %s:%u %s not readlocked\n",             \
+                      __FILE__, __LINE__, #lk);                        \
+       clear_bit(smp_processor_id(), &(lk)->read_locked_map);          \
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6_logging.h linux-2.4.20/include/linux/netfilter_ipv6/ip6_logging.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6_logging.h        Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6_logging.h    Wed Sep 24 09:16:23 2003
+@@ -0,0 +1,20 @@
++/* IPv6 macros for the nternal logging interface. */
++#ifndef __IP6_LOGGING_H
++#define __IP6_LOGGING_H
++
++#ifdef __KERNEL__
++#include <linux/socket.h>
++#include <linux/netfilter_logging.h>
++
++#define nf_log_ip6_packet(pskb,hooknum,in,out,fmt,args...) \
++      nf_log_packet(AF_INET6,pskb,hooknum,in,out,fmt,##args)
++
++#define nf_log_ip6(pfh,len,fmt,args...) \
++      nf_log(AF_INET6,pfh,len,fmt,##args)
++
++#define nf_ip6_log_register(logging) nf_log_register(AF_INET6,logging)
++#define nf_ip6_log_unregister(logging) nf_log_unregister(AF_INET6,logging)
++      
++#endif /*__KERNEL__*/
++
++#endif /*__IP6_LOGGING_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6_tables.h linux-2.4.20/include/linux/netfilter_ipv6/ip6_tables.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6_tables.h Mon Feb 25 19:38:13 2002
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6_tables.h     Wed Sep 24 09:16:14 2003
+@@ -449,6 +449,9 @@
+                                 struct ip6t_table *table,
+                                 void *userdata);
++/* Check for an extension */
++extern int ip6t_ext_hdr(u8 nexthdr);
++
+ #define IP6T_ALIGN(s) (((s) + (__alignof__(struct ip6t_entry)-1)) & ~(__alignof__(struct ip6t_entry)-1))
+ #endif /*__KERNEL__*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_HL.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_HL.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_HL.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_HL.h        Wed Sep 24 09:16:28 2003
+@@ -0,0 +1,22 @@
++/* Hop Limit modification module for ip6tables
++ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
++ * Based on HW's TTL module */
++
++#ifndef _IP6T_HL_H
++#define _IP6T_HL_H
++
++enum {
++      IP6T_HL_SET = 0,
++      IP6T_HL_INC,
++      IP6T_HL_DEC
++};
++
++#define IP6T_HL_MAXMODE       IP6T_HL_DEC
++
++struct ip6t_HL_info {
++      u_int8_t        mode;
++      u_int8_t        hop_limit;
++};
++
++
++#endif
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_IMQ.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_IMQ.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_IMQ.h   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_IMQ.h       Wed Sep 24 09:17:21 2003
+@@ -0,0 +1,8 @@
++#ifndef _IP6T_IMQ_H
++#define _IP6T_IMQ_H
++
++struct ip6t_imq_info {
++      unsigned int todev;     /* target imq device */
++};
++
++#endif /* _IP6T_IMQ_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_REJECT.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_REJECT.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_REJECT.h        Tue Jun 20 21:32:27 2000
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_REJECT.h    Wed Sep 24 09:16:36 2003
+@@ -2,15 +2,17 @@
+ #define _IP6T_REJECT_H
+ enum ip6t_reject_with {
+-      IP6T_ICMP_NET_UNREACHABLE,
+-      IP6T_ICMP_HOST_UNREACHABLE,
+-      IP6T_ICMP_PROT_UNREACHABLE,
+-      IP6T_ICMP_PORT_UNREACHABLE,
+-      IP6T_ICMP_ECHOREPLY
++      IP6T_ICMP6_NO_ROUTE,
++      IP6T_ICMP6_ADM_PROHIBITED,
++      IP6T_ICMP6_NOT_NEIGHBOUR,
++      IP6T_ICMP6_ADDR_UNREACH,
++      IP6T_ICMP6_PORT_UNREACH,
++      IP6T_ICMP6_ECHOREPLY,
++      IP6T_TCP_RESET
+ };
+ struct ip6t_reject_info {
+       enum ip6t_reject_with with;      /* reject type */
+ };
+-#endif /*_IPT_REJECT_H*/
++#endif /*_IP6T_REJECT_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_ROUTE.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_ROUTE.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_ROUTE.h Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_ROUTE.h     Wed Sep 24 09:17:27 2003
+@@ -0,0 +1,22 @@
++/* Header file for iptables ip6t_ROUTE target
++ *
++ * (C) 2003 by Cédric de Launois <delaunois@info.ucl.ac.be>
++ *
++ * This software is distributed under GNU GPL v2, 1991
++ */
++#ifndef _IPT_ROUTE_H_target
++#define _IPT_ROUTE_H_target
++
++#define IP6T_ROUTE_IFNAMSIZ 16
++
++struct ip6t_route_target_info {
++      char      oif[IP6T_ROUTE_IFNAMSIZ];     /* Output Interface Name */
++      char      iif[IP6T_ROUTE_IFNAMSIZ];     /* Input Interface Name  */
++      u_int32_t gw[4];                        /* IPv6 address of gateway */
++      u_int8_t  flags;
++};
++
++/* Values for "flags" field */
++#define IP6T_ROUTE_CONTINUE        0x01
++
++#endif /*_IP6T_ROUTE_H_target*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_ah.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_ah.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_ah.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_ah.h        Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,30 @@
++#ifndef _IP6T_AH_H
++#define _IP6T_AH_H
++
++struct ip6t_ah
++{
++      u_int32_t spis[2];                      /* Security Parameter Index */
++      u_int32_t hdrlen;                       /* Header Length */
++      u_int8_t  hdrres;                       /* Test of the Reserved Filed */
++      u_int8_t  invflags;                     /* Inverse flags */
++};
++
++#define IP6T_AH_SPI 0x01
++#define IP6T_AH_LEN 0x02
++#define IP6T_AH_RES 0x04
++
++/* Values for "invflags" field in struct ip6t_ah. */
++#define IP6T_AH_INV_SPI               0x01    /* Invert the sense of spi. */
++#define IP6T_AH_INV_LEN               0x02    /* Invert the sense of length. */
++#define IP6T_AH_INV_MASK      0x03    /* All possible flags. */
++
++#define MASK_HOPOPTS    128
++#define MASK_DSTOPTS    64
++#define MASK_ROUTING    32
++#define MASK_FRAGMENT   16
++#define MASK_AH         8
++#define MASK_ESP        4
++#define MASK_NONE       2
++#define MASK_PROTO      1
++
++#endif /*_IP6T_AH_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_condition.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_condition.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_condition.h     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_condition.h Wed Sep 24 09:17:36 2003
+@@ -0,0 +1,11 @@
++#ifndef __IP6T_CONDITION_MATCH__
++#define __IP6T_CONDITION_MATCH__
++
++#define CONDITION6_NAME_LEN  32
++
++struct condition6_info {
++      char name[CONDITION6_NAME_LEN];
++      int  invert;
++};
++
++#endif
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_esp.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_esp.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_esp.h   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_esp.h       Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,23 @@
++#ifndef _IP6T_ESP_H
++#define _IP6T_ESP_H
++
++struct ip6t_esp
++{
++      u_int32_t spis[2];                      /* Security Parameter Index */
++      u_int8_t  invflags;                     /* Inverse flags */
++};
++
++#define MASK_HOPOPTS    128
++#define MASK_DSTOPTS    64
++#define MASK_ROUTING    32
++#define MASK_FRAGMENT   16
++#define MASK_AH         8
++#define MASK_ESP        4
++#define MASK_NONE       2
++#define MASK_PROTO      1
++
++/* Values for "invflags" field in struct ip6t_esp. */
++#define IP6T_ESP_INV_SPI              0x01    /* Invert the sense of spi. */
++#define IP6T_ESP_INV_MASK     0x01    /* All possible flags. */
++
++#endif /*_IP6T_ESP_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_frag.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_frag.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_frag.h  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_frag.h      Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,33 @@
++#ifndef _IP6T_FRAG_H
++#define _IP6T_FRAG_H
++
++struct ip6t_frag
++{
++      u_int32_t ids[2];                       /* Security Parameter Index */
++      u_int32_t hdrlen;                       /* Header Length */
++      u_int8_t  flags;                        /*  */
++      u_int8_t  invflags;                     /* Inverse flags */
++};
++
++#define IP6T_FRAG_IDS                 0x01
++#define IP6T_FRAG_LEN                 0x02
++#define IP6T_FRAG_RES                 0x04
++#define IP6T_FRAG_FST                 0x08
++#define IP6T_FRAG_MF                  0x10
++#define IP6T_FRAG_NMF                 0x20
++
++/* Values for "invflags" field in struct ip6t_frag. */
++#define IP6T_FRAG_INV_IDS     0x01    /* Invert the sense of ids. */
++#define IP6T_FRAG_INV_LEN     0x02    /* Invert the sense of length. */
++#define IP6T_FRAG_INV_MASK    0x03    /* All possible flags. */
++
++#define MASK_HOPOPTS    128
++#define MASK_DSTOPTS    64
++#define MASK_ROUTING    32
++#define MASK_FRAGMENT   16
++#define MASK_AH         8
++#define MASK_ESP        4
++#define MASK_NONE       2
++#define MASK_PROTO      1
++
++#endif /*_IP6T_FRAG_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_fuzzy.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_fuzzy.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_fuzzy.h Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_fuzzy.h     Wed Sep 24 09:16:45 2003
+@@ -0,0 +1,21 @@
++#ifndef _IP6T_FUZZY_H
++#define _IP6T_FUZZY_H
++
++#include <linux/param.h>
++#include <linux/types.h>
++
++#define MAXFUZZYRATE 10000000
++#define MINFUZZYRATE 3
++
++struct ip6t_fuzzy_info {
++      u_int32_t minimum_rate;
++      u_int32_t maximum_rate;
++      u_int32_t packets_total;
++      u_int32_t bytes_total;
++      u_int32_t previous_time;
++      u_int32_t present_time;
++      u_int32_t mean_rate;
++      u_int8_t acceptance_rate;
++};
++
++#endif /*_IP6T_FUZZY_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_hl.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_hl.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_hl.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_hl.h        Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,22 @@
++/* ip6tables module for matching the Hop Limit value
++ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
++ * Based on HW's ttl module */
++
++#ifndef _IP6T_HL_H
++#define _IP6T_HL_H
++
++enum {
++      IP6T_HL_EQ = 0,         /* equals */
++      IP6T_HL_NE,             /* not equals */
++      IP6T_HL_LT,             /* less than */
++      IP6T_HL_GT,             /* greater than */
++};
++
++
++struct ip6t_hl_info {
++      u_int8_t        mode;
++      u_int8_t        hop_limit;
++};
++
++
++#endif
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_ipv6header.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_ipv6header.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_ipv6header.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_ipv6header.h        Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,27 @@
++/* ipv6header match - matches IPv6 packets based
++on whether they contain certain headers */
++
++/* Original idea: Brad Chapman 
++ * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */
++
++
++#ifndef __IPV6HEADER_H
++#define __IPV6HEADER_H
++
++struct ip6t_ipv6header_info
++{
++      u_int8_t matchflags;
++      u_int8_t invflags;
++      u_int8_t modeflag;
++};
++
++#define MASK_HOPOPTS    128
++#define MASK_DSTOPTS    64
++#define MASK_ROUTING    32
++#define MASK_FRAGMENT   16
++#define MASK_AH         8
++#define MASK_ESP        4
++#define MASK_NONE       2
++#define MASK_PROTO      1
++
++#endif /* __IPV6HEADER_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_nth.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_nth.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_nth.h   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_nth.h       Wed Sep 24 09:16:55 2003
+@@ -0,0 +1,19 @@
++#ifndef _IP6T_NTH_H
++#define _IP6T_NTH_H
++
++#include <linux/param.h>
++#include <linux/types.h>
++
++#ifndef IP6T_NTH_NUM_COUNTERS
++#define IP6T_NTH_NUM_COUNTERS 16
++#endif
++
++struct ip6t_nth_info {
++      u_int8_t every;
++      u_int8_t not;
++      u_int8_t startat;
++      u_int8_t counter;
++      u_int8_t packet;
++};
++
++#endif /*_IP6T_NTH_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_opts.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_opts.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_opts.h  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_opts.h      Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,32 @@
++#ifndef _IP6T_OPTS_H
++#define _IP6T_OPTS_H
++
++#define IP6T_OPTS_OPTSNR 16
++
++struct ip6t_opts
++{
++      u_int32_t hdrlen;                       /* Header Length */
++      u_int8_t flags;                         /*  */
++      u_int8_t invflags;                      /* Inverse flags */
++      u_int16_t opts[IP6T_OPTS_OPTSNR];       /* opts */
++      u_int8_t optsnr;                        /* Nr of OPts */
++};
++
++#define IP6T_OPTS_LEN                 0x01
++#define IP6T_OPTS_OPTS                0x02
++#define IP6T_OPTS_NSTRICT     0x04
++
++/* Values for "invflags" field in struct ip6t_rt. */
++#define IP6T_OPTS_INV_LEN     0x01    /* Invert the sense of length. */
++#define IP6T_OPTS_INV_MASK    0x01    /* All possible flags. */
++
++#define MASK_HOPOPTS    128
++#define MASK_DSTOPTS    64
++#define MASK_ROUTING    32
++#define MASK_FRAGMENT   16
++#define MASK_AH         8
++#define MASK_ESP        4
++#define MASK_NONE       2
++#define MASK_PROTO      1
++
++#endif /*_IP6T_OPTS_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_owner.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_owner.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_owner.h Tue Jun 20 21:32:27 2000
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_owner.h     Wed Sep 24 09:17:52 2003
+@@ -6,12 +6,14 @@
+ #define IP6T_OWNER_GID        0x02
+ #define IP6T_OWNER_PID        0x04
+ #define IP6T_OWNER_SID        0x08
++#define IP6T_OWNER_COMM 0x10
+ struct ip6t_owner_info {
+     uid_t uid;
+     gid_t gid;
+     pid_t pid;
+     pid_t sid;
++    char comm[16];
+     u_int8_t match, invert;   /* flags */
+ };
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_random.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_random.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_random.h        Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_random.h    Wed Sep 24 09:17:07 2003
+@@ -0,0 +1,11 @@
++#ifndef _IP6T_RAND_H
++#define _IP6T_RAND_H
++
++#include <linux/param.h>
++#include <linux/types.h>
++
++struct ip6t_rand_info {
++      u_int8_t average;
++};
++
++#endif /*_IP6T_RAND_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_rt.h linux-2.4.20/include/linux/netfilter_ipv6/ip6t_rt.h
+--- linux-2.4.20.org/include/linux/netfilter_ipv6/ip6t_rt.h    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_ipv6/ip6t_rt.h        Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,42 @@
++#ifndef _IP6T_RT_H
++#define _IP6T_RT_H
++
++/*#include <linux/in6.h>*/
++
++#define IP6T_RT_HOPS 16
++
++struct ip6t_rt
++{
++      u_int32_t rt_type;                      /* Routing Type */
++      u_int32_t segsleft[2];                  /* Segments Left */
++      u_int32_t hdrlen;                       /* Header Length */
++      u_int8_t  flags;                        /*  */
++      u_int8_t  invflags;                     /* Inverse flags */
++      struct in6_addr addrs[IP6T_RT_HOPS];    /* Hops */
++      u_int8_t addrnr;                        /* Nr of Addresses */
++};
++
++#define IP6T_RT_TYP           0x01
++#define IP6T_RT_SGS           0x02
++#define IP6T_RT_LEN           0x04
++#define IP6T_RT_RES           0x08
++#define IP6T_RT_FST_MASK      0x30
++#define IP6T_RT_FST           0x10
++#define IP6T_RT_FST_NSTRICT   0x20
++
++/* Values for "invflags" field in struct ip6t_rt. */
++#define IP6T_RT_INV_TYP               0x01    /* Invert the sense of type. */
++#define IP6T_RT_INV_SGS               0x02    /* Invert the sense of Segments. */
++#define IP6T_RT_INV_LEN               0x04    /* Invert the sense of length. */
++#define IP6T_RT_INV_MASK      0x07    /* All possible flags. */
++
++#define MASK_HOPOPTS    128
++#define MASK_DSTOPTS    64
++#define MASK_ROUTING    32
++#define MASK_FRAGMENT   16
++#define MASK_AH         8
++#define MASK_ESP        4
++#define MASK_NONE       2
++#define MASK_PROTO      1
++
++#endif /*_IP6T_RT_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/netfilter_logging.h linux-2.4.20/include/linux/netfilter_logging.h
+--- linux-2.4.20.org/include/linux/netfilter_logging.h Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/netfilter_logging.h     Wed Sep 24 09:16:23 2003
+@@ -0,0 +1,33 @@
++/* Internal logging interface, which relies on the real 
++   LOG target modules */
++#ifndef __LINUX_NETFILTER_LOGGING_H
++#define __LINUX_NETFILTER_LOGGING_H
++
++#ifdef __KERNEL__
++#include <asm/atomic.h>
++
++struct nf_logging_t {
++      void (*nf_log_packet)(struct sk_buff **pskb,
++                            unsigned int hooknum,
++                            const struct net_device *in,
++                            const struct net_device *out,
++                            const char *prefix);
++      void (*nf_log)(char *pfh, size_t len,
++                     const char *prefix);
++};
++
++extern void nf_log_register(int pf, const struct nf_logging_t *logging);
++extern void nf_log_unregister(int pf, const struct nf_logging_t *logging);
++
++extern void nf_log_packet(int pf,
++                        struct sk_buff **pskb,
++                        unsigned int hooknum,
++                        const struct net_device *in,
++                        const struct net_device *out,
++                        const char *fmt, ...);
++extern void nf_log(int pf,
++                 char *pfh, size_t len,
++                 const char *fmt, ...);
++#endif /*__KERNEL__*/
++
++#endif /*__LINUX_NETFILTER_LOGGING_H*/
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/nfnetlink.h linux-2.4.20/include/linux/nfnetlink.h
+--- linux-2.4.20.org/include/linux/nfnetlink.h Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/nfnetlink.h     Wed Sep 24 09:17:51 2003
+@@ -0,0 +1,158 @@
++#ifndef _NFNETLINK_H
++#define _NFNETLINK_H
++#include <linux/types.h>
++
++/* Generic structure for encapsulation optional netfilter information.
++ * It is reminiscent of sockaddr, but with sa_family replaced
++ * with attribute type. 
++ * ! This should someday be put somewhere generic as now rtnetlink and
++ * ! nfnetlink use the same attributes methods. - J. Schulist.
++ */
++
++struct nfattr
++{
++      unsigned short  nfa_len;
++      unsigned short  nfa_type;
++};
++
++#define NFA_ALIGNTO     4
++#define NFA_ALIGN(len)        (((len) + NFA_ALIGNTO - 1) & ~(NFA_ALIGNTO - 1))
++#define NFA_OK(nfa,len)       ((len) > 0 && (nfa)->nfa_len >= sizeof(struct nfattr) \
++      && (nfa)->nfa_len <= (len))
++#define NFA_NEXT(nfa,attrlen) ((attrlen) -= NFA_ALIGN((nfa)->nfa_len), \
++      (struct nfattr *)(((char *)(nfa)) + NFA_ALIGN((nfa)->nfa_len)))
++#define NFA_LENGTH(len)       (NFA_ALIGN(sizeof(struct nfattr)) + (len))
++#define NFA_SPACE(len)        NFA_ALIGN(NFA_LENGTH(len))
++#define NFA_DATA(nfa)   ((void *)(((char *)(nfa)) + NFA_LENGTH(0)))
++#define NFA_PAYLOAD(nfa) ((int)((nfa)->nfa_len) - NFA_LENGTH(0))
++
++/* General form of address family dependent message.
++ */
++struct nfgenmsg {
++      unsigned char nfgen_family;
++};
++
++#if 0
++struct iptgenmsg {
++      unsigned char   iptgen_family;
++      char            iptgen_table[IPT_TABLE_MAXNAMELEN];
++};
++
++struct iptmsg {
++      unsigned char   iptm_family;
++      char            iptm_table[IPT_TABLE_MAXNAMELEN];
++      char            iptm_chain[IPT_FUNCTION_MAXNAMELEN];
++      unsigned int    iptm_entry_num;
++};
++
++enum iptattr_type_t
++{
++      IPTA_UNSPEC,    /* [none] I don't know (unspecified). */
++      IPTA_IP,        /* [ipt_ip] */
++      IPTA_NFCACHE,   /* [u_int] */
++      IPTA_COUNTERS,  /* [ipt_counters] */
++      IPTA_MATCH,     /* [ipt_info] */
++      IPTA_TARGET,    /* [ipt_info] */
++      IPTA_MAX = IPTA_TARGET
++};
++
++struct ipta_info {
++      u_int16_t       size;
++      char            name[IPT_FUNCTION_MAXNAMELEN];
++      unsigned char   data[0];
++};
++
++#define NFM_IPTA(n)   ((struct nfattr *)(((char *)(n)) \
++      + NLMSG_ALIGN(sizeof(struct iptmsg))))
++
++#endif
++
++#define NFM_NFA(n)      ((struct nfattr *)(((char *)(n)) \
++        + NLMSG_ALIGN(sizeof(struct nfgenmsg))))
++#define NFM_PAYLOAD(n)  NLMSG_PAYLOAD(n, sizeof(struct nfgenmsg))
++
++
++#ifndef NETLINK_NETFILTER
++#define NETLINK_NETFILTER     6
++#endif
++
++/* netfilter netlink message types are split in two pieces:
++ * 8 bit subsystem, 8bit operation.
++ */
++
++#define NFNL_SUBSYS_ID(x)     ((x & 0xff00) >> 8)
++#define NFNL_MSG_TYPE(x)      (x & 0x00ff)
++
++enum nfnl_subsys_id {
++      NFNL_SUBSYS_NONE = 0,
++      NFNL_SUBSYS_CTNETLINK,
++      NFNL_SUBSYS_CTNETLINK_EXP,
++      NFNL_SUBSYS_IPTNETLINK,
++      NFNL_SUBSYS_QUEUE,
++      NFNL_SUBSYS_ULOG,
++      NFNL_SUBSYS_COUNT,
++};
++
++#ifdef __KERNEL__
++
++#include <linux/capability.h>
++
++struct nfnl_callback
++{
++      kernel_cap_t cap_required; /* capabilities required for this msg */
++      int (*call)(struct sock *nl, struct sk_buff *skb, 
++              struct nlmsghdr *nlh, int *errp);
++};
++
++struct nfnetlink_subsystem
++{
++      /* Internal use. */
++      struct list_head list;
++      
++      const char *name;
++      __u8 subsys_id;         /* nfnetlink subsystem ID */
++      __u8 cb_count;          /* number of callbacks */
++      u_int32_t attr_count;   /* number of nfattr's */
++      struct nfnl_callback cb[0]; /* callback for individual types */
++};
++
++extern void __nfa_fill(struct sk_buff *skb, int attrtype,
++        int attrlen, const void *data);
++#define NFA_PUT(skb, attrtype, attrlen, data) \
++({ if (skb_tailroom(skb) < (int)NFA_SPACE(attrlen)) goto nfattr_failure; \
++   __nfa_fill(skb, attrtype, attrlen, data); })
++
++extern struct semaphore nfnl_sem;
++#define nfnl_exlock()         do { } while(0)
++#define nfnl_exunlock()               do { } while(0)
++#define nfnl_exlock_nowait()  (0)
++
++#define nfnl_shlock()         down(&nfnl_sem)
++#define nfnl_shlock_nowait()  down_trylock(&nfnl_sem)
++
++#ifndef CONFIG_NF_NETLINK
++#define nfnl_shunlock()               up(&nfnl_sem)
++#else
++#define nfnl_shunlock()               do { up(&nfnl_sem); \
++                                      if(nfnl && nfnl->receive_queue.qlen) \
++                                              nfnl->data_ready(nfnl, 0); \
++                              } while(0)
++#endif
++
++extern void nfnl_lock(void);
++extern void nfnl_unlock(void);
++
++extern struct nfnetlink_subsystem *nfnetlink_subsys_alloc(int cb_count);
++extern int nfnetlink_subsys_register(struct nfnetlink_subsystem *n);
++extern int nfnetlink_subsys_unregister(struct nfnetlink_subsystem *n);
++
++extern int nfnetlink_check_attributes(struct nfnetlink_subsystem *subsys,
++                                    struct nlmsghdr *nlh, 
++                                    struct nfattr *cda[]);
++extern int nfattr_parse(struct nfattr *tb[], int maxattr, 
++                      struct nfattr *nfa, int len);
++extern int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, 
++                        int echo);
++
++#endif        /* __KERNEL__ */
++#endif        /* _NFNETLINK_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/nfnetlink_conntrack.h linux-2.4.20/include/linux/nfnetlink_conntrack.h
+--- linux-2.4.20.org/include/linux/nfnetlink_conntrack.h       Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/include/linux/nfnetlink_conntrack.h   Wed Sep 24 09:17:51 2003
+@@ -0,0 +1,84 @@
++#ifndef _NFNETLINK_CONNTRACK_H
++#define _NFNETLINK_CONNTRACK_H
++#include <linux/nfnetlink.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++//#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
++
++/* CTNETLINK for ip_conntrack */
++
++enum cntl_msg_types {
++      CTNL_MSG_NEWCONNTRACK,
++      CTNL_MSG_GETCONNTRACK,
++      CTNL_MSG_DELCONNTRACK,
++
++      CTNL_MSG_NEWEXPECT,
++      CTNL_MSG_GETEXPECT,
++      CTNL_MSG_DELEXPECT,
++      CTNL_MSG_CONFIRMEXPECT,
++
++      CTNL_MSG_COUNT,
++};
++
++/* ctnetlink attribute types.
++ */
++enum ctattr_type_t
++{
++      CTA_UNSPEC,     /* [none] I don't know (unspecified). */
++      CTA_ORIG,       /* [ip_conntrack_tuple] Original tuple. */
++      CTA_RPLY,       /* [ip_conntrack_tuple] Reply tuple. */
++      CTA_IIF,        /* [char] Input interface name (ie eth0). */
++      CTA_OIF,        /* [char] Output interface name (ie eth1). */
++      CTA_STATUS,     /* [unsigned long] Status of connection. */
++      CTA_INFO,       /* [unsigned long] Information (ctinfo). */
++      CTA_PROTOINFO,  /* [cta_proto] Protocol specific ct information. */
++      CTA_HELPINFO,   /* [cta_help] Helper specific information. */
++      CTA_NATINFO,    /* [cta_nat] Any NAT transformations. */
++      CTA_TIMEOUT,    /* [unsigne long] timer */
++
++      CTA_EXP_TIMEOUT,/* [fixme] timer */
++      CTA_EXP_TUPLE,  /* [ip_conntrack_tuple] Expected tuple */
++      CTA_EXP_MASK,   /* [ip_conntrack_tuple] Mask for EXP_TUPLE */
++      CTA_EXP_SEQNO,  /* [u_int32_t] sequence number */
++      CTA_EXP_PROTO,  /* [cta_exp_proto] */
++      CTA_EXP_HELP,   /* [cta_exp_help] */
++
++        CTA_MAX = CTA_EXP_HELP
++};
++
++/* Attribute specific data structures.
++ */
++
++#ifdef CONFIG_IP_NF_NAT_NEEDED
++#include <linux/netfilter_ipv4/ip_nat.h>
++struct cta_nat {
++      unsigned int num_manips;
++      struct ip_nat_info_manip manips[IP_NAT_MAX_MANIPS];
++};
++#endif /* CONFIG_IP_NF_NAT_NEEDED */
++
++struct cta_proto {
++      unsigned char num_proto;        /* Protocol number IPPROTO_X */
++      union ip_conntrack_proto proto;
++};
++
++struct cta_help {
++      struct ip_conntrack_tuple tuple;
++      struct ip_conntrack_tuple mask;
++      char name[31];                  /* name of conntrack helper */
++      union ip_conntrack_help help;
++};
++
++/* ctnetlink multicast groups: reports any change of ctinfo,
++ * ctstatus, or protocol state change.
++ */
++#define NFGRP_IPV4_CT_TCP     0x01
++#define NFGRP_IPV4_CT_UDP     0x02
++#define NFGRP_IPV4_CT_ICMP    0x04
++#define NFGRP_IPV4_CT_OTHER   0x08
++
++#define NFGRP_IPV6_CT_TCP       0x10
++#define NFGRP_IPV6_CT_UDP       0x20
++#define NFGRP_IPV6_CT_ICMP      0x40
++#define NFGRP_IPV6_CT_OTHER   0x80
++
++#endif /* _NFNETLINK_CONNTRACK_H */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/linux/sysctl.h linux-2.4.20/include/linux/sysctl.h
+--- linux-2.4.20.org/include/linux/sysctl.h    Wed Sep 24 08:52:38 2003
++++ linux-2.4.20/include/linux/sysctl.h        Wed Sep 24 09:16:22 2003
+@@ -235,6 +235,7 @@
+       NET_IPV4_NEIGH=17,
+       NET_IPV4_ROUTE=18,
+       NET_IPV4_FIB_HASH=19,
++      NET_IPV4_NETFILTER=20,
+       NET_IPV4_TCP_TIMESTAMPS=33,
+       NET_IPV4_TCP_WINDOW_SCALING=34,
+@@ -343,6 +344,24 @@
+       NET_IPV4_CONF_MEDIUM_ID=14,
+ };
++/* /proc/sys/net/ipv4/netfilter */
++enum
++{
++      NET_IPV4_NF_CONNTRACK_MAX=1,
++      NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT=2,
++      NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV=3,
++      NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED=4,
++      NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT=5,
++      NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT=6,
++      NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK=7,
++      NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT=8,
++      NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE=9,
++      NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT=10,
++      NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT_STREAM=11,
++      NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT=12,
++      NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT=13,
++};
++ 
+ /* /proc/sys/net/ipv6 */
+ enum {
+       NET_IPV6_CONF=16,
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/net/ip6_route.h linux-2.4.20/include/net/ip6_route.h
+--- linux-2.4.20.org/include/net/ip6_route.h   Mon Dec 11 21:30:48 2000
++++ linux-2.4.20/include/net/ip6_route.h       Wed Sep 24 09:16:14 2003
+@@ -31,6 +31,8 @@
+ extern struct dst_entry *     ip6_route_output(struct sock *sk,
+                                                struct flowi *fl);
++extern int                    ip6_route_me_harder(struct sk_buff *skb);
++
+ extern void                   ip6_route_init(void);
+ extern void                   ip6_route_cleanup(void);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/net/tcp.h linux-2.4.20/include/net/tcp.h
+--- linux-2.4.20.org/include/net/tcp.h Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/include/net/tcp.h     Wed Sep 24 09:17:52 2003
+@@ -140,6 +140,7 @@
+ extern void tcp_bucket_unlock(struct sock *sk);
+ extern int tcp_port_rover;
+ extern struct sock *tcp_v4_lookup_listener(u32 addr, unsigned short hnum, int dif);
++extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 hnum, int dif);
+ /* These are AF independent. */
+ static __inline__ int tcp_bhashfn(__u16 lport)
+diff -Nur --exclude '*.orig' linux-2.4.20.org/include/net/udp.h linux-2.4.20/include/net/udp.h
+--- linux-2.4.20.org/include/net/udp.h Thu Nov 22 19:47:15 2001
++++ linux-2.4.20/include/net/udp.h     Wed Sep 24 09:17:52 2003
+@@ -69,6 +69,8 @@
+ extern int    udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+ extern int    udp_disconnect(struct sock *sk, int flags);
++extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif);
++
+ extern struct udp_mib udp_statistics[NR_CPUS*2];
+ #define UDP_INC_STATS(field)          SNMP_INC_STATS(udp_statistics, field)
+ #define UDP_INC_STATS_BH(field)               SNMP_INC_STATS_BH(udp_statistics, field)
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/core/netfilter.c linux-2.4.20/net/core/netfilter.c
+--- linux-2.4.20.org/net/core/netfilter.c      Sat Aug  3 00:39:46 2002
++++ linux-2.4.20/net/core/netfilter.c  Wed Sep 24 09:17:50 2003
+@@ -8,9 +8,12 @@
+  *
+  * February 2000: Modified by James Morris to have 1 queue per protocol.
+  * 15-Mar-2000:   Added NF_REPEAT --RR.
++ * 08-May-2003:         Internal logging interface added by Jozsef Kadlecsik.
+  */
+ #include <linux/config.h>
++#include <linux/kernel.h>
+ #include <linux/netfilter.h>
++#include <linux/netfilter_logging.h>
+ #include <net/protocol.h>
+ #include <linux/init.h>
+ #include <linux/skbuff.h>
+@@ -57,6 +60,10 @@
+       void *data;
+ } queue_handler[NPROTO];
++/**
++ * nf_register_hook - Register with a netfilter hook
++ * @reg: Hook operations to be registered
++ */
+ int nf_register_hook(struct nf_hook_ops *reg)
+ {
+       struct list_head *i;
+@@ -73,6 +80,10 @@
+       return 0;
+ }
++/**
++ * nf_unregister_hook - Unregister from a netfilter hook
++ * @reg: hook operations to be unregistered
++ */
+ void nf_unregister_hook(struct nf_hook_ops *reg)
+ {
+       br_write_lock_bh(BR_NETPROTO_LOCK);
+@@ -373,6 +384,18 @@
+       return NF_ACCEPT;
+ }
++/**
++ * nf_register_queue_handler - Registere a queue handler with netfilter
++ * @pf: protocol family
++ * @outfn: function called by core to enqueue a packet
++ * @data: opaque parameter, passed through
++ *
++ * This function registers a queue handler with netfilter.  There can only
++ * be one queue handler for every protocol family.
++ *
++ * A queue handler _must_ reinject every packet via nf_reinject, no
++ * matter what.
++ */
+ int nf_register_queue_handler(int pf, nf_queue_outfn_t outfn, void *data)
+ {      
+       int ret;
+@@ -390,7 +413,12 @@
+       return ret;
+ }
+-/* The caller must flush their queue before this */
++/**
++ * nf_unregister_queue_handler - Unregister queue handler from netfilter
++ * @pf: protocol family
++ *
++ * The caller must flush their queue before unregistering
++ */
+ int nf_unregister_queue_handler(int pf)
+ {
+       br_write_lock_bh(BR_NETPROTO_LOCK);
+@@ -502,6 +530,15 @@
+       return ret;
+ }
++/**
++ * nf_reinject - Reinject a packet from a queue handler
++ * @skb: the packet to be reinjected
++ * @info: info which was passed to the outfn() of the queue handler
++ * @verdict: verdict (NF_ACCEPT, ...) for this packet
++ *
++ * This is the function called by a queue handler to reinject a
++ * packet.
++ */
+ void nf_reinject(struct sk_buff *skb, struct nf_info *info,
+                unsigned int verdict)
+ {
+@@ -563,67 +600,134 @@
+ {
+       struct iphdr *iph = (*pskb)->nh.iph;
+       struct rtable *rt;
+-      struct rt_key key = { dst:iph->daddr,
+-                            src:iph->saddr,
+-                            oif:(*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0,
+-                            tos:RT_TOS(iph->tos)|RTO_CONN,
+-#ifdef CONFIG_IP_ROUTE_FWMARK
+-                            fwmark:(*pskb)->nfmark
+-#endif
+-                          };
+-      struct net_device *dev_src = NULL;
+-      int err;
+-
+-      /* accomodate ip_route_output_slow(), which expects the key src to be
+-         0 or a local address; however some non-standard hacks like
+-         ipt_REJECT.c:send_reset() can cause packets with foreign
+-           saddr to be appear on the NF_IP_LOCAL_OUT hook -MB */
+-      if(key.src && !(dev_src = ip_dev_find(key.src)))
+-              key.src = 0;
+-
+-      if ((err=ip_route_output_key(&rt, &key)) != 0) {
+-              printk("route_me_harder: ip_route_output_key(dst=%u.%u.%u.%u, src=%u.%u.%u.%u, oif=%d, tos=0x%x, fwmark=0x%lx) error %d\n",
+-                      NIPQUAD(iph->daddr), NIPQUAD(iph->saddr),
+-                      (*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0,
+-                      RT_TOS(iph->tos)|RTO_CONN,
++      struct rt_key key = {};
++      struct dst_entry *odst;
++      unsigned int hh_len;
++
++      /* some non-standard hacks like ipt_REJECT.c:send_reset() can cause
++       * packets with foreign saddr to be appear on the NF_IP_LOCAL_OUT hook.
++       */
++      if (inet_addr_type(iph->saddr) == RTN_LOCAL) {
++              key.dst = iph->daddr;
++              key.src = iph->saddr;
++              key.oif = (*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0;
++              key.tos = RT_TOS(iph->tos);
+ #ifdef CONFIG_IP_ROUTE_FWMARK
+-                      (*pskb)->nfmark,
+-#else
+-                      0UL,
++              key.fwmark = (*pskb)->nfmark;
+ #endif
+-                      err);
+-              goto out;
+-      }
++              if (ip_route_output_key(&rt, &key) != 0)
++                      return -1;
+-      /* Drop old route. */
+-      dst_release((*pskb)->dst);
+-
+-      (*pskb)->dst = &rt->u.dst;
++              /* Drop old route. */
++              dst_release((*pskb)->dst);
++              (*pskb)->dst = &rt->u.dst;
++      } else {
++              /* non-local src, find valid iif to satisfy
++               * rp-filter when calling ip_route_input. */
++              key.dst = iph->saddr;
++              if (ip_route_output_key(&rt, &key) != 0)
++                      return -1;
++
++              odst = (*pskb)->dst;
++              if (ip_route_input(*pskb, iph->daddr, iph->saddr,
++                                 RT_TOS(iph->tos), rt->u.dst.dev) != 0) {
++                      dst_release(&rt->u.dst);
++                      return -1;
++              }
++              dst_release(&rt->u.dst);
++              dst_release(odst);
++      }
++      
++      if ((*pskb)->dst->error)
++              return -1;
+       /* Change in oif may mean change in hh_len. */
+-      if (skb_headroom(*pskb) < (*pskb)->dst->dev->hard_header_len) {
++      hh_len = (*pskb)->dst->dev->hard_header_len;
++      if (skb_headroom(*pskb) < hh_len) {
+               struct sk_buff *nskb;
+-              nskb = skb_realloc_headroom(*pskb,
+-                                          (*pskb)->dst->dev->hard_header_len);
+-              if (!nskb) {
+-                      err = -ENOMEM;
+-                      goto out;
+-              }
++              nskb = skb_realloc_headroom(*pskb, hh_len);
++              if (!nskb)
++                      return -1;
+               if ((*pskb)->sk)
+                       skb_set_owner_w(nskb, (*pskb)->sk);
+               kfree_skb(*pskb);
+               *pskb = nskb;
+       }
+-out:
+-      if (dev_src)
+-              dev_put(dev_src);
+-
+-      return err;
++      return 0;
+ }
+ #endif /*CONFIG_INET*/
++/* Internal logging interface, which relies on the real 
++   LOG target modules */
++
++#define NF_LOG_PREFIXLEN              128
++
++static struct nf_logging_t nf_logging[NPROTO] = {};
++static int reported = 0;
++
++void nf_log_register(int pf, const struct nf_logging_t *logging)
++{
++      br_write_lock_bh(BR_NETPROTO_LOCK);
++      if (!nf_logging[pf].nf_log_packet) {
++              nf_logging[pf].nf_log_packet = logging->nf_log_packet;
++              nf_logging[pf].nf_log = logging->nf_log;
++      }
++      br_write_unlock_bh(BR_NETPROTO_LOCK);
++}             
++
++void nf_log_unregister(int pf, const struct nf_logging_t *logging)
++{
++      br_write_lock_bh(BR_NETPROTO_LOCK);
++      if (nf_logging[pf].nf_log_packet == logging->nf_log_packet) {
++              nf_logging[pf].nf_log_packet = NULL;
++              nf_logging[pf].nf_log = NULL;
++      }
++      br_write_unlock_bh(BR_NETPROTO_LOCK);
++}             
++
++void nf_log_packet(int pf,
++                 struct sk_buff **pskb,
++                 unsigned int hooknum,
++                 const struct net_device *in,
++                 const struct net_device *out,
++                 const char *fmt, ...)
++{
++      va_list args;
++      char prefix[NF_LOG_PREFIXLEN];
++      
++      if (nf_logging[pf].nf_log_packet) {
++              va_start(args, fmt);
++              vsnprintf(prefix, sizeof(prefix), fmt, args);
++              va_end(args);
++              nf_logging[pf].nf_log_packet(pskb, hooknum, in, out, prefix);
++      } else if (!reported) {
++              printk(KERN_WARNING "nf_log_packet: can\'t log yet, "
++                     "no backend logging module loaded in!\n");
++              reported++;
++      }
++}
++
++void nf_log(int pf,
++          char *pfh, size_t len,
++          const char *fmt, ...)
++{
++      va_list args;
++      char prefix[NF_LOG_PREFIXLEN];
++      
++      if (nf_logging[pf].nf_log) {
++              va_start(args, fmt);
++              vsnprintf(prefix, sizeof(prefix), fmt, args);
++              va_end(args);
++              nf_logging[pf].nf_log(pfh, len, prefix);
++      } else if (!reported) {
++              printk(KERN_WARNING "nf_log: can\'t log yet, "
++                     "no backend logging module loaded in!\n");
++              reported++;
++      }
++}
++
+ /* This does not belong here, but ipt_REJECT needs it if connection
+    tracking in use: without this, connection may not be in hash table,
+    and hence manufactured ICMP or RST packets will not be associated
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/Config.in linux-2.4.20/net/ipv4/netfilter/Config.in
+--- linux-2.4.20.org/net/ipv4/netfilter/Config.in      Wed Sep 24 08:52:39 2003
++++ linux-2.4.20/net/ipv4/netfilter/Config.in  Wed Sep 24 09:18:12 2003
+@@ -4,10 +4,29 @@
+ mainmenu_option next_comment
+ comment '  IP: Netfilter Configuration'
++tristate 'Netfilter netlink interface' CONFIG_IP_NF_NETLINK
++
+ tristate 'Connection tracking (required for masq/NAT)' CONFIG_IP_NF_CONNTRACK
+ if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then
++  if [ "$CONFIG_IP_NF_CONNTRACK" = "y" ]; then
++    dep_tristate '  Connection tracking netlink interface' CONFIG_IP_NF_NETLINK_CONNTRACK $CONFIG_IP_NF_NETLINK
++  else
++    dep_tristate '  Connection tracking netlink interface' CONFIG_IP_NF_NETLINK_CONNTRACK $CONFIG_IP_NF_CONNTRACK
++  fi
+   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 '  RSH protocol support' CONFIG_IP_NF_RSH $CONFIG_IP_NF_CONNTRACK
++  dep_tristate '  H.323 (netmeeting) support' CONFIG_IP_NF_H323 $CONFIG_IP_NF_CONNTRACK
++  dep_tristate '  Eggdrop bot support' CONFIG_IP_NF_EGG $CONFIG_IP_NF_CONNTRACK
++  bool '  Connection mark tracking support' CONFIG_IP_NF_CONNTRACK_MARK
++  dep_tristate '  Amanda protocol support' CONFIG_IP_NF_AMANDA $CONFIG_IP_NF_CONNTRACK
++  dep_tristate '  TFTP protocol support' CONFIG_IP_NF_TFTP $CONFIG_IP_NF_CONNTRACK
+   dep_tristate '  IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK
++  dep_tristate '  Quake III protocol support' CONFIG_IP_NF_QUAKE3 $CONFIG_IP_NF_CONNTRACK
++  dep_tristate '  GRE protocol support' CONFIG_IP_NF_CT_PROTO_GRE $CONFIG_IP_NF_CONNTRACK
++  dep_tristate '   PPTP protocol support' CONFIG_IP_NF_PPTP $CONFIG_IP_NF_CT_PROTO_GRE
++  dep_tristate '  MMS protocol support' CONFIG_IP_NF_MMS $CONFIG_IP_NF_CONNTRACK
++  dep_tristate '  CuSeeMe protocol support' CONFIG_IP_NF_CUSEEME $CONFIG_IP_NF_CONNTRACK
+ fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+@@ -15,39 +34,85 @@
+ fi
+ tristate 'IP tables support (required for filtering/masq/NAT)' CONFIG_IP_NF_IPTABLES
+ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; then
++  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++    tristate 'raw table support (required for NOTRACK/TRACE)' CONFIG_IP_NF_RAW $CONFIG_IP_NF_IPTABLES
++  fi
+ # The simple matches.
++  if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then
++    dep_tristate '  RPC match support' CONFIG_IP_NF_MATCH_RPC $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES
++  fi
+   dep_tristate '  limit match support' CONFIG_IP_NF_MATCH_LIMIT $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  quota match support' CONFIG_IP_NF_MATCH_QUOTA $CONFIG_IP_NF_IPTABLES
++
++  dep_tristate '  IP address pool support' CONFIG_IP_NF_POOL $CONFIG_IP_NF_IPTABLES
++  if [ "$CONFIG_IP_NF_POOL" = "y" -o "$CONFIG_IP_NF_POOL" = "m" ]; then
++    bool '    enable statistics on pool usage' CONFIG_IP_POOL_STATISTICS n
++  fi
++
++  dep_tristate '  IP range match support' CONFIG_IP_NF_MATCH_IPRANGE $CONFIG_IP_NF_IPTABLES
+   dep_tristate '  MAC address match support' CONFIG_IP_NF_MATCH_MAC $CONFIG_IP_NF_IPTABLES
+   dep_tristate '  Packet type match support' CONFIG_IP_NF_MATCH_PKTTYPE $CONFIG_IP_NF_IPTABLES
+   dep_tristate '  netfilter MARK match support' CONFIG_IP_NF_MATCH_MARK $CONFIG_IP_NF_IPTABLES
+   dep_tristate '  Multiple port match support' CONFIG_IP_NF_MATCH_MULTIPORT $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  Multiple port with ranges match support' CONFIG_IP_NF_MATCH_MPORT $CONFIG_IP_NF_IPTABLES
+   dep_tristate '  TOS match support' CONFIG_IP_NF_MATCH_TOS $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  condition match support' CONFIG_IP_NF_MATCH_CONDITION $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  TIME match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_TIME $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  random match support' CONFIG_IP_NF_MATCH_RANDOM $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  psd match support' CONFIG_IP_NF_MATCH_PSD $CONFIG_IP_NF_IPTABLES
++  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++    dep_tristate '  OSF match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OSF $CONFIG_IP_NF_IPTABLES
++  fi
++  dep_tristate '  Nth match support' CONFIG_IP_NF_MATCH_NTH $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  IPV4OPTIONS match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_IPV4OPTIONS $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  fuzzy match support' CONFIG_IP_NF_MATCH_FUZZY $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  recent match support' CONFIG_IP_NF_MATCH_RECENT $CONFIG_IP_NF_IPTABLES
+   dep_tristate '  ECN match support' CONFIG_IP_NF_MATCH_ECN $CONFIG_IP_NF_IPTABLES
+  
+   dep_tristate '  DSCP match support' CONFIG_IP_NF_MATCH_DSCP $CONFIG_IP_NF_IPTABLES
+  
+   dep_tristate '  AH/ESP match support' CONFIG_IP_NF_MATCH_AH_ESP $CONFIG_IP_NF_IPTABLES
+   dep_tristate '  LENGTH match support' CONFIG_IP_NF_MATCH_LENGTH $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  U32 match support' CONFIG_IP_NF_MATCH_U32 $CONFIG_IP_NF_U32
+   dep_tristate '  TTL match support' CONFIG_IP_NF_MATCH_TTL $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  address type match support' CONFIG_IP_NF_MATCH_ADDRTYPE $CONFIG_IP_NF_IPTABLES
+   dep_tristate '  tcpmss match support' CONFIG_IP_NF_MATCH_TCPMSS $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  realm match support' CONFIG_IP_NF_MATCH_REALM $CONFIG_IP_NF_IPTABLES
+   dep_tristate '  stealth match support' CONFIG_IP_NF_MATCH_STEALTH $CONFIG_IP_NF_IPTABLES
+   if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then
+     dep_tristate '  Helper match support' CONFIG_IP_NF_MATCH_HELPER $CONFIG_IP_NF_IPTABLES
+   fi
+   if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then
+     dep_tristate '  Connection state match support' CONFIG_IP_NF_MATCH_STATE $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES 
++    if [ "$CONFIG_IP_NF_CONNTRACK_MARK" != "n" ]; then
++      dep_tristate '  Connection mark match support' CONFIG_IP_NF_MATCH_CONNMARK $CONFIG_IP_NF_IPTABLES
++    fi
++    dep_tristate '  Connections/IP limit match support' CONFIG_IP_NF_MATCH_CONNLIMIT $CONFIG_IP_NF_IPTABLES
+     dep_tristate '  Connection tracking match support' CONFIG_IP_NF_MATCH_CONNTRACK $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES 
+   fi
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+     dep_tristate '  Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES
++    dep_tristate '  String match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_STRING $CONFIG_IP_NF_IPTABLES
+     dep_tristate '  Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES
+   fi
+ # The targets
++  if [ "$CONFIG_IP_NF_RAW" != "n" ]; then
++    if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then
++      dep_tristate '  NOTRACK target support' CONFIG_IP_NF_TARGET_NOTRACK $CONFIG_IP_NF_RAW
++    fi
++    dep_tristate '  TRACE target support' CONFIG_IP_NF_TARGET_TRACE $CONFIG_IP_NF_RAW
++    if [ "$CONFIG_IP_NF_TARGET_TRACE" != "n" ]; then
++      define_bool CONFIG_IP_NF_TARGET_TRACE_NEEDED y
++    fi
++  fi
+   dep_tristate '  Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES 
+   if [ "$CONFIG_IP_NF_FILTER" != "n" ]; then
+     dep_tristate '    REJECT target support' CONFIG_IP_NF_TARGET_REJECT $CONFIG_IP_NF_FILTER
++dep_tristate '    NETLINK target support' CONFIG_IP_NF_TARGET_NETLINK $CONFIG_IP_NF_FILTER
++    dep_tristate '    IPV4OPTSSTRIP target support' CONFIG_IP_NF_TARGET_IPV4OPTSSTRIP $CONFIG_IP_NF_FILTER
+     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+       dep_tristate '    MIRROR target support (EXPERIMENTAL)' CONFIG_IP_NF_TARGET_MIRROR $CONFIG_IP_NF_FILTER
++      dep_tristate '    TARPIT target support (EXPERIMENTAL)' CONFIG_IP_NF_TARGET_TARPIT $CONFIG_IP_NF_FILTER
+     fi
+   fi
+@@ -57,6 +122,45 @@
+       define_bool CONFIG_IP_NF_NAT_NEEDED y
+       dep_tristate '    MASQUERADE target support' CONFIG_IP_NF_TARGET_MASQUERADE $CONFIG_IP_NF_NAT
+       dep_tristate '    REDIRECT target support' CONFIG_IP_NF_TARGET_REDIRECT $CONFIG_IP_NF_NAT
++      # If they want talk, set to $CONFIG_IP_NF_NAT (m or y), 
++      # or $CONFIG_IP_NF_TALK (m or y), whichever is weaker.  Argh.
++      if [ "$CONFIG_IP_NF_TALK" = "m" ]; then
++      define_tristate CONFIG_IP_NF_NAT_TALK m
++      else
++        if [ "$CONFIG_IP_NF_TALK" = "y" ]; then
++          define_tristate CONFIG_IP_NF_NAT_TALK $CONFIG_IP_NF_NAT
++        fi
++      fi
++      if [ "$CONFIG_IP_NF_H323" = "m" ]; then
++       define_tristate CONFIG_IP_NF_NAT_H323 m
++      else
++        if [ "$CONFIG_IP_NF_H323" = "y" ]; then
++          define_tristate CONFIG_IP_NF_NAT_H323 $CONFIG_IP_NF_NAT
++        fi
++      fi
++      dep_tristate '    SAME target support' CONFIG_IP_NF_TARGET_SAME $CONFIG_IP_NF_NAT
++      dep_tristate '    NETMAP target support' CONFIG_IP_NF_TARGET_NETMAP $CONFIG_IP_NF_NAT
++      if [ "$CONFIG_IP_NF_AMANDA" = "m" ]; then
++        define_tristate CONFIG_IP_NF_NAT_AMANDA m
++      else
++        if [ "$CONFIG_IP_NF_AMANDA" = "y" ]; then
++          define_tristate CONFIG_IP_NF_NAT_AMANDA $CONFIG_IP_NF_NAT
++        fi
++      fi
++      if [ "$CONFIG_IP_NF_PPTP" = "m" ]; then
++        define_tristate CONFIG_IP_NF_NAT_PPTP m
++      else
++        if [ "$CONFIG_IP_NF_PPTP" = "y" ]; then
++          define_tristate CONFIG_IP_NF_NAT_PPTP $CONFIG_IP_NF_NAT
++        fi
++      fi
++      if [ "$CONFIG_IP_NF_CT_PROTO_GRE" = "m" ]; then
++        define_tristate CONFIG_IP_NF_NAT_PROTO_GRE m
++      else
++        if [ "$CONFIG_IP_NF_CT_PROTO_GRE" = "y" ]; then
++        define_tristate CONFIG_IP_NF_NAT_PROTO_GRE $CONFIG_IP_NF_NAT
++      fi
++      fi
+       bool '    NAT of local connections (READ HELP)' CONFIG_IP_NF_NAT_LOCAL
+       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+         dep_tristate '    Basic SNMP-ALG support (EXPERIMENTAL)' CONFIG_IP_NF_NAT_SNMP_BASIC $CONFIG_IP_NF_NAT
+@@ -68,6 +172,27 @@
+           define_tristate CONFIG_IP_NF_NAT_IRC $CONFIG_IP_NF_NAT
+         fi
+       fi
++      if [ "$CONFIG_IP_NF_QUAKE3" = "m" ]; then
++        define_tristate CONFIG_IP_NF_NAT_QUAKE3 m
++      else
++        if [ "$CONFIG_IP_NF_QUAKE3" = "y" ]; then
++         define_tristate CONFIG_IP_NF_NAT_QUAKE3 $CONFIG_IP_NF_NAT
++        fi
++      fi
++      if [ "$CONFIG_IP_NF_MMS" = "m" ]; then
++        define_tristate CONFIG_IP_NF_NAT_MMS m
++      else
++        if [ "$CONFIG_IP_NF_MMS" = "y" ]; then
++          define_tristate CONFIG_IP_NF_NAT_MMS $CONFIG_IP_NF_NAT
++        fi
++      fi
++      if [ "$CONFIG_IP_NF_CUSEEME" = "m" ]; then
++        define_tristate CONFIG_IP_NF_NAT_CUSEEME m
++      else
++        if [ "$CONFIG_IP_NF_CUSEEME" = "y" ]; then
++         define_tristate CONFIG_IP_NF_NAT_CUSEEME $CONFIG_IP_NF_NAT
++        fi
++      fi
+       # If they want FTP, set to $CONFIG_IP_NF_NAT (m or y), 
+       # or $CONFIG_IP_NF_FTP (m or y), whichever is weaker.  Argh.
+       if [ "$CONFIG_IP_NF_FTP" = "m" ]; then
+@@ -77,6 +202,13 @@
+           define_tristate CONFIG_IP_NF_NAT_FTP $CONFIG_IP_NF_NAT
+         fi
+       fi
++      if [ "$CONFIG_IP_NF_TFTP" = "m" ]; then
++      define_tristate CONFIG_IP_NF_NAT_TFTP m
++      else
++        if [ "$CONFIG_IP_NF_TFTP" = "y" ]; then
++          define_tristate CONFIG_IP_NF_NAT_TFTP $CONFIG_IP_NF_NAT
++        fi
++      fi
+     fi
+   fi
+@@ -88,8 +220,19 @@
+     dep_tristate '    DSCP target support' CONFIG_IP_NF_TARGET_DSCP $CONFIG_IP_NF_MANGLE
+  
+     dep_tristate '    MARK target support' CONFIG_IP_NF_TARGET_MARK $CONFIG_IP_NF_MANGLE
++    dep_tristate '    ROUTE target support' CONFIG_IP_NF_TARGET_ROUTE $CONFIG_IP_NF_MANGLE
++ 
++    dep_tristate '    IPMARK target support' CONFIG_IP_NF_TARGET_IPMARK $CONFIG_IP_NF_MANGLE
++    dep_tristate '    IMQ target support' CONFIG_IP_NF_TARGET_IMQ $CONFIG_IP_NF_MANGLE
++    dep_tristate '    CLASSIFY target support (EXPERIMENTAL)' CONFIG_IP_NF_TARGET_CLASSIFY $CONFIG_IP_NF_FILTER
+   fi
+   dep_tristate '  LOG target support' CONFIG_IP_NF_TARGET_LOG $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  XOR target support' CONFIG_IP_NF_TARGET_XOR $CONFIG_IP_NF_IPTABLES
++  dep_tristate '  TCPLAG target support' CONFIG_IP_NF_TARGET_TCPLAG $CONFIG_IP_NF_IPTABLES
++  if [ "$CONFIG_IP_NF_CONNTRACK_MARK" != "n" ]; then
++    dep_tristate '  CONNMARK target support' CONFIG_IP_NF_TARGET_CONNMARK $CONFIG_IP_NF_IPTABLES
++  fi
++  dep_tristate '  TTL target support' CONFIG_IP_NF_TARGET_TTL $CONFIG_IP_NF_IPTABLES
+   dep_tristate '  ULOG target support' CONFIG_IP_NF_TARGET_ULOG $CONFIG_IP_NF_IPTABLES
+   dep_tristate '  TCPMSS target support' CONFIG_IP_NF_TARGET_TCPMSS $CONFIG_IP_NF_IPTABLES
+ fi
+@@ -98,6 +241,9 @@
+ if [ "$CONFIG_IP_NF_ARPTABLES" != "n" ]; then
+   dep_tristate '  ARP packet filtering' CONFIG_IP_NF_ARPFILTER $CONFIG_IP_NF_ARPTABLES 
+ fi
++if [ "$CONFIG_IP_NF_ARPTABLES" != "n" ]; then
++  dep_tristate '  ARP payload mangling' CONFIG_IP_NF_ARP_MANGLE $CONFIG_IP_NF_ARPTABLES
++fi
+ # Backwards compatibility modules: only if you don't build in the others.
+ if [ "$CONFIG_IP_NF_CONNTRACK" != "y" ]; then
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/Makefile linux-2.4.20/net/ipv4/netfilter/Makefile
+--- linux-2.4.20.org/net/ipv4/netfilter/Makefile       Wed Sep 24 08:52:39 2003
++++ linux-2.4.20/net/ipv4/netfilter/Makefile   Wed Sep 24 09:18:13 2003
+@@ -28,23 +28,85 @@
+ ipfwadm-objs          := $(ip_nf_compat-objs) ipfwadm_core.o
+ ipchains-objs         := $(ip_nf_compat-objs) ipchains_core.o
++# netfilter netlink interface
++obj-$(CONFIG_IP_NF_NETLINK) += nfnetlink.o
++ifdef CONFIG_IP_NF_NETLINK
++      export-objs += nfnetlink.o
++endif
++
++# nfnetlink modules
++obj-$(CONFIG_IP_NF_NETLINK_CONNTRACK) += nfnetlink_conntrack.o
++
+ # connection tracking
+ obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o
++# talk protocol support
++obj-$(CONFIG_IP_NF_TALK) += ip_conntrack_talk.o
++ifdef CONFIG_IP_NF_TALK
++      export-objs += ip_conntrack_talk.o
++endif
++obj-$(CONFIG_IP_NF_NAT_TALK) += ip_nat_talk.o
++
++ 
++# H.323 support
++obj-$(CONFIG_IP_NF_H323) += ip_conntrack_h323.o
++ifdef CONFIG_IP_NF_H323
++      export-objs += ip_conntrack_h323.o
++endif
++obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o
++
++
++# connection tracking protocol helpers
++obj-$(CONFIG_IP_NF_CT_PROTO_GRE) += ip_conntrack_proto_gre.o
++ifdef CONFIG_IP_NF_CT_PROTO_GRE
++      export-objs += ip_conntrack_proto_gre.o
++endif
++
++# NAT protocol helpers
++obj-$(CONFIG_IP_NF_NAT_PROTO_GRE) += ip_nat_proto_gre.o
++
+ # connection tracking helpers
++obj-$(CONFIG_IP_NF_QUAKE3) += ip_conntrack_quake3.o
++ifdef CONFIG_IP_NF_NAT_QUAKE3
++      export-objs += ip_conntrack_quake3.o
++endif
++obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o
++ifdef CONFIG_IP_NF_NAT_PPTP
++      export-objs += ip_conntrack_pptp.o
++endif
++obj-$(CONFIG_IP_NF_MMS) += ip_conntrack_mms.o
++ifdef CONFIG_IP_NF_MMS
++      export-objs += ip_conntrack_mms.o
++endif
++obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o
++ifdef CONFIG_IP_NF_AMANDA
++      export-objs += ip_conntrack_amanda.o
++endif
++
++obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o
+ obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o
+-ifdef CONFIG_IP_NF_NAT_FTP
++obj-$(CONFIG_IP_NF_RSH) += ip_conntrack_rsh.o
++
++obj-$(CONFIG_IP_NF_EGG) += ip_conntrack_egg.o
++
++ifdef CONFIG_IP_NF_FTP
+       export-objs += ip_conntrack_ftp.o
+ endif
+ obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o
+-ifdef CONFIG_IP_NF_NAT_IRC
++ifdef CONFIG_IP_NF_IRC
+       export-objs += ip_conntrack_irc.o
+ endif
+ # NAT helpers 
++obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o
++obj-$(CONFIG_IP_NF_NAT_CUSEEME) += ip_nat_cuseeme.o
++obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o
++obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o
+ obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o
+ obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o
++obj-$(CONFIG_IP_NF_NAT_QUAKE3) += ip_nat_quake3.o
++obj-$(CONFIG_IP_NF_NAT_MMS) += ip_nat_mms.o
+ # generic IP tables 
+ obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o
+@@ -53,46 +115,103 @@
+ obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o
+ obj-$(CONFIG_IP_NF_MANGLE) += iptable_mangle.o
+ obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o
++obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o
+ # matches
++obj-$(CONFIG_IP_NF_MATCH_RPC) += ip_conntrack_rpc_tcp.o ip_conntrack_rpc_udp.o ipt_rpc.o
++export-objs += ip_conntrack_rpc_tcp.o ip_conntrack_rpc_udp.o
++
+ obj-$(CONFIG_IP_NF_MATCH_HELPER) += ipt_helper.o
+ obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o
++obj-$(CONFIG_IP_NF_MATCH_QUOTA) += ipt_quota.o
++obj-$(CONFIG_IP_NF_MATCH_IPRANGE) += ipt_iprange.o
+ obj-$(CONFIG_IP_NF_MATCH_MARK) += ipt_mark.o
++obj-$(CONFIG_IP_NF_POOL) += ipt_pool.o ipt_POOL.o ip_pool.o
+ obj-$(CONFIG_IP_NF_MATCH_MAC) += ipt_mac.o
+ obj-$(CONFIG_IP_NF_MATCH_PKTTYPE) += ipt_pkttype.o
+ obj-$(CONFIG_IP_NF_MATCH_MULTIPORT) += ipt_multiport.o
++
++obj-$(CONFIG_IP_NF_MATCH_MPORT) += ipt_mport.o
++
+ obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o
+ obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o
++obj-$(CONFIG_IP_NF_MATCH_CONDITION) += ipt_condition.o
++
++obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o
++
++
++obj-$(CONFIG_IP_NF_MATCH_RANDOM) += ipt_random.o
++
++obj-$(CONFIG_IP_NF_MATCH_PSD) += ipt_psd.o
++
++obj-$(CONFIG_IP_NF_MATCH_OSF) += ipt_osf.o
++
++
++obj-$(CONFIG_IP_NF_MATCH_NTH) += ipt_nth.o
++
++obj-$(CONFIG_IP_NF_MATCH_IPV4OPTIONS) += ipt_ipv4options.o
++
++
++obj-$(CONFIG_IP_NF_MATCH_FUZZY) += ipt_fuzzy.o
++
++obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o
++
+ obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o
+ obj-$(CONFIG_IP_NF_MATCH_DSCP) += ipt_dscp.o
+ obj-$(CONFIG_IP_NF_MATCH_AH_ESP) += ipt_ah.o ipt_esp.o
+ obj-$(CONFIG_IP_NF_MATCH_LENGTH) += ipt_length.o
++obj-$(CONFIG_IP_NF_MATCH_U32) += ipt_u32.o
++
++
+ obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
+ obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o
++obj-$(CONFIG_IP_NF_MATCH_CONNMARK) += ipt_connmark.o
++obj-$(CONFIG_IP_NF_MATCH_CONNLIMIT) += ipt_connlimit.o
+ obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o
+ obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o
++obj-$(CONFIG_IP_NF_MATCH_STRING) += ipt_string.o
+ obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o
++obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o
++obj-$(CONFIG_IP_NF_MATCH_REALM) += ipt_realm.o
+ obj-$(CONFIG_IP_NF_MATCH_STEALTH) += ipt_stealth.o
+ # targets
+ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+ obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o
++obj-$(CONFIG_IP_NF_TARGET_TARPIT) += ipt_TARPIT.o
++obj-$(CONFIG_IP_NF_TARGET_CLASSIFY) += ipt_CLASSIFY.o
+ obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o
+ obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o
+ obj-$(CONFIG_IP_NF_TARGET_DSCP) += ipt_DSCP.o
+ obj-$(CONFIG_IP_NF_TARGET_MARK) += ipt_MARK.o
++obj-$(CONFIG_IP_NF_TARGET_IPMARK) += ipt_IPMARK.o
++obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o
+ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
+ obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
++obj-$(CONFIG_IP_NF_TARGET_ROUTE) += ipt_ROUTE.o
++obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o
++obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
+ obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o
+ obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o
++obj-$(CONFIG_IP_NF_TARGET_XOR) += ipt_XOR.o
++obj-$(CONFIG_IP_NF_TARGET_TCPLAG) += ipt_TCPLAG.o
++obj-$(CONFIG_IP_NF_TARGET_CONNMARK) += ipt_CONNMARK.o
++obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o
++
++obj-$(CONFIG_IP_NF_TARGET_NETLINK) += ipt_NETLINK.o
++
++obj-$(CONFIG_IP_NF_TARGET_IPV4OPTSSTRIP) += ipt_IPV4OPTSSTRIP.o
+ obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
+ obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt_TCPMSS.o
++obj-$(CONFIG_IP_NF_TARGET_TRACE) += ipt_TRACE.o
++obj-$(CONFIG_IP_NF_TARGET_NOTRACK) += ipt_NOTRACK.o
+ # generic ARP tables
+ obj-$(CONFIG_IP_NF_ARPTABLES) += arp_tables.o
++obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_mangle.o
+ # just filtering instance of ARP tables for now
+ obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/arp_tables.c linux-2.4.20/net/ipv4/netfilter/arp_tables.c
+--- linux-2.4.20.org/net/ipv4/netfilter/arp_tables.c   Sat Aug  3 00:39:46 2002
++++ linux-2.4.20/net/ipv4/netfilter/arp_tables.c       Wed Sep 24 09:16:17 2003
+@@ -136,6 +136,7 @@
+               dprintf("ARP hardware address length mismatch.\n");
+               dprintf("ar_hln: %02x info->arhln: %02x info->arhln_mask: %02x\n",
+                       arphdr->ar_hln, arpinfo->arhln, arpinfo->arhln_mask);
++              return 0;
+       }
+       src_devaddr = arpptr;
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/arpt_mangle.c linux-2.4.20/net/ipv4/netfilter/arpt_mangle.c
+--- linux-2.4.20.org/net/ipv4/netfilter/arpt_mangle.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/arpt_mangle.c      Wed Sep 24 09:16:17 2003
+@@ -0,0 +1,101 @@
++/* module that allows mangling of the arp payload */
++#include <linux/module.h>
++#include <linux/netfilter_arp/arpt_mangle.h>
++#include <net/sock.h>
++
++static unsigned int
++target(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in,
++   const struct net_device *out, const void *targinfo, void *userinfo)
++{
++      const struct arpt_mangle *mangle = targinfo;
++      struct arphdr *arp;
++      unsigned char *arpptr;
++      int pln, hln;
++
++      if (skb_shared(*pskb) || skb_cloned(*pskb)) {
++              struct sk_buff *nskb;
++
++              nskb = skb_copy(*pskb, GFP_ATOMIC);
++              if (!nskb)
++                      return NF_DROP;
++              if ((*pskb)->sk)
++                      skb_set_owner_w(nskb, (*pskb)->sk);
++              kfree_skb(*pskb);
++              *pskb = nskb;
++      }
++
++      arp = (*pskb)->nh.arph;
++      arpptr = (*pskb)->nh.raw + sizeof(*arp);
++      pln = arp->ar_pln;
++      hln = arp->ar_hln;
++      /* We assume that hln was checked in the match */
++      if (mangle->flags & ARPT_MANGLE_SDEV) {
++              if (ARPT_DEV_ADDR_LEN_MAX < hln ||
++                 (arpptr + hln > (**pskb).tail))
++                      return NF_DROP;
++              memcpy(arpptr, mangle->src_devaddr, hln);
++      }
++      arpptr += hln;
++      if (mangle->flags & ARPT_MANGLE_SIP) {
++              if (ARPT_MANGLE_ADDR_LEN_MAX < pln ||
++                 (arpptr + pln > (**pskb).tail))
++                      return NF_DROP;
++              memcpy(arpptr, &mangle->u_s.src_ip, pln);
++      }
++      arpptr += pln;
++      if (mangle->flags & ARPT_MANGLE_TDEV) {
++              if (ARPT_DEV_ADDR_LEN_MAX < hln ||
++                 (arpptr + hln > (**pskb).tail))
++                      return NF_DROP;
++              memcpy(arpptr, mangle->tgt_devaddr, hln);
++      }
++      arpptr += hln;
++      if (mangle->flags & ARPT_MANGLE_TIP) {
++              if (ARPT_MANGLE_ADDR_LEN_MAX < pln ||
++                 (arpptr + pln > (**pskb).tail))
++                      return NF_DROP;
++              memcpy(arpptr, &mangle->u_t.tgt_ip, pln);
++      }
++      return mangle->target;
++}
++
++static int
++checkentry(const char *tablename, const struct arpt_entry *e, void *targinfo,
++   unsigned int targinfosize, unsigned int hook_mask)
++{
++      const struct arpt_mangle *mangle = targinfo;
++
++      if (mangle->flags & ~ARPT_MANGLE_MASK ||
++          !(mangle->flags & ARPT_MANGLE_MASK))
++              return 0;
++
++      if (mangle->target != NF_DROP && mangle->target != NF_ACCEPT &&
++         mangle->target != ARPT_CONTINUE)
++              return 0;
++      return 1;
++}
++
++static struct arpt_target arpt_mangle_reg
++= {
++        .name         = "mangle",
++        .target               = target,
++        .checkentry   = checkentry,
++        .me           = THIS_MODULE,
++};
++
++static int __init init(void)
++{
++      if (arpt_register_target(&arpt_mangle_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      arpt_unregister_target(&arpt_mangle_reg);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_amanda.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_amanda.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_amanda.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_amanda.c      Wed Sep 24 09:16:24 2003
+@@ -0,0 +1,234 @@
++/* Amanda extension for IP connection tracking, Version 0.2
++ * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca>
++ * based on HW's ip_conntrack_irc.c as well as other modules
++ *
++ *      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_amanda.o [master_timeout=n]
++ *    
++ *    Where master_timeout is the timeout (in seconds) of the master
++ *    connection (port 10080).  This defaults to 5 minutes but if
++ *    your clients take longer than 5 minutes to do their work
++ *    before getting back to the Amanda server, you can increase
++ *    this value.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++#include <net/udp.h>
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_amanda.h>
++
++static unsigned int master_timeout = 300;
++
++MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>");
++MODULE_DESCRIPTION("Amanda connection tracking module");
++MODULE_LICENSE("GPL");
++MODULE_PARM(master_timeout, "i");
++MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
++
++DECLARE_LOCK(ip_amanda_lock);
++struct module *ip_conntrack_amanda = THIS_MODULE;
++
++#define MAXMATCHLEN   6
++struct conn conns[NUM_MSGS] = {
++      {"DATA ", 5},
++      {"MESG ", 5},
++      {"INDEX ", 6},
++};
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++
++/* FIXME: This should be in userspace.  Later. */
++static int help(const struct iphdr *iph, size_t len,
++              struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
++{
++      struct udphdr *udph = (void *)iph + iph->ihl * 4;
++      u_int32_t udplen = len - iph->ihl * 4;
++      u_int32_t datalen = udplen - sizeof(struct udphdr);
++      char *data = (char *)udph + sizeof(struct udphdr);
++      char *datap = data;
++      char *data_limit = (char *) data + datalen;
++      int dir = CTINFO2DIR(ctinfo);
++      struct ip_ct_amanda *info =
++                              (struct ip_ct_amanda *)&ct->help.ct_ftp_info;
++
++      /* Can't track connections formed before we registered */
++      if (!info)
++              return NF_ACCEPT;
++
++      /* increase the UDP timeout of the master connection as replies from
++       * Amanda clients to the server can be quite delayed */
++      ip_ct_refresh(ct, master_timeout * HZ);
++
++      /* If packet is coming from Amanda server */
++      if (dir == IP_CT_DIR_ORIGINAL)
++              return NF_ACCEPT;
++
++      /* Not whole UDP header? */
++      if (udplen < sizeof(struct udphdr)) {
++              printk("ip_conntrack_amanda_help: udplen = %u\n",
++                     (unsigned)udplen);
++              return NF_ACCEPT;
++      }
++
++      /* Checksum invalid?  Ignore. */
++      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;
++      }
++      
++      /* Search for the CONNECT string */
++      while (data < data_limit) {
++              if (!memcmp(data, "CONNECT ", 8)) {
++                      break;
++              }
++              data++;
++      }
++      if (memcmp(data, "CONNECT ", 8))
++              return NF_ACCEPT;
++
++      DEBUGP("ip_conntrack_amanda_help: CONNECT found in connection "
++                 "%u.%u.%u.%u:%u %u.%u.%u.%u:%u\n",
++                 NIPQUAD(iph->saddr), htons(udph->source),
++                 NIPQUAD(iph->daddr), htons(udph->dest));
++      data += 8;
++      while (*data != 0x0a && data < data_limit) {
++
++              int i;
++
++              for (i = 0; i < NUM_MSGS; i++) {
++                      if (!memcmp(data, conns[i].match,
++                                 conns[i].matchlen)) {
++
++                              char *portchr;
++                              struct ip_conntrack_expect expect;
++                              struct ip_ct_amanda_expect
++                                  *exp_amanda_info =
++                                      &expect.help.exp_amanda_info;
++
++                              memset(&expect, 0, sizeof(expect));
++
++                              data += conns[i].matchlen;
++                              /* this is not really tcp, but let's steal an
++                               * idea from a tcp stream helper :-)
++                               */
++                              // XXX expect.seq = data - datap;
++                              exp_amanda_info->offset = data - datap;
++// XXX DEBUGP("expect.seq = %p - %p = %d\n", data, datap, expect.seq);
++DEBUGP("exp_amanda_info->offset = %p - %p = %d\n", data, datap, exp_amanda_info->offset);
++                              portchr = data;
++                              exp_amanda_info->port =
++                                  simple_strtoul(data, &data, 10);
++                              exp_amanda_info->len = data - portchr;
++
++                              /* eat whitespace */
++                              while (*data == ' ')
++                                      data++;
++                              DEBUGP ("ip_conntrack_amanda_help: "
++                                      "CONNECT %s request with port "
++                                      "%u found\n", conns[i].match,
++                                      exp_amanda_info->port);
++
++                              LOCK_BH(&ip_amanda_lock);
++
++                              expect.tuple = ((struct ip_conntrack_tuple)
++                                              { { ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip,
++                                                { 0 } },
++                                              { ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip,
++                                                { htons(exp_amanda_info->port) },
++                                        IPPROTO_TCP }});
++                              expect.mask = ((struct ip_conntrack_tuple)
++                                                         { { 0, { 0 } },
++                                                               { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
++
++                              expect.expectfn = NULL;
++
++                              DEBUGP ("ip_conntrack_amanda_help: "
++                                      "expect_related: %u.%u.%u.%u:%u - "
++                                      "%u.%u.%u.%u:%u\n",
++                                      NIPQUAD(expect.tuple.src.ip),
++                                      ntohs(expect.tuple.src.u.tcp.port),
++                                      NIPQUAD(expect.tuple.dst.ip),
++                                      ntohs(expect.tuple.dst.u.tcp.port));
++                              if (ip_conntrack_expect_related(ct, &expect) ==
++                                  -EEXIST) {
++                                      ;
++                                      /* this must be a packet being resent */
++                                      /* XXX - how do I get the
++                                       *       ip_conntrack_expect that
++                                       *       already exists so that I can
++                                       *       update the .seq so that the
++                                       *       nat module rewrites the port
++                                       *       numbers?
++                                       *       Perhaps I should use the
++                                       *       exp_amanda_info instead of
++                                       *       .seq.
++                                       */
++                              }
++                              UNLOCK_BH(&ip_amanda_lock);
++                      } /* if memcmp(conns) */
++              } /* for .. NUM_MSGS */
++              data++;
++      } /* while (*data != 0x0a && data < data_limit) */
++
++      return NF_ACCEPT;
++}
++
++static struct ip_conntrack_helper amanda_helper;
++
++static void fini(void)
++{
++      DEBUGP("ip_ct_amanda: unregistering helper for port 10080\n");
++      ip_conntrack_helper_unregister(&amanda_helper);
++}
++
++static int __init init(void)
++{
++      int ret;
++
++      amanda_helper.tuple.src.u.udp.port = htons(10080);
++      amanda_helper.tuple.dst.protonum = IPPROTO_UDP;
++      amanda_helper.mask.src.u.udp.port = 0xFFFF;
++      amanda_helper.mask.dst.protonum = 0xFFFF;
++      amanda_helper.max_expected = NUM_MSGS;
++      amanda_helper.timeout = 180;
++      amanda_helper.flags = IP_CT_HELPER_F_REUSE_EXPECT;
++      amanda_helper.me = ip_conntrack_amanda;
++      amanda_helper.help = help;
++      amanda_helper.name = "amanda";
++
++      DEBUGP("ip_ct_amanda: registering helper for port 10080\n");
++
++      ret = ip_conntrack_helper_register(&amanda_helper);
++
++      if (ret) {
++              printk("ip_ct_amanda: ERROR registering helper\n");
++              fini();
++              return -EBUSY;
++      }
++      return 0;
++}
++
++EXPORT_SYMBOL(ip_amanda_lock);
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_core.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_core.c    Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_core.c        Wed Sep 24 09:18:16 2003
+@@ -4,6 +4,7 @@
+ /* (c) 1999 Paul `Rusty' Russell.  Licenced under the GNU General
+  * Public Licence. 
++ * (C) 2000-2003 by the netfilter core team <coreteam@netfilter.org>
+  *
+  * 23 Apr 2001: Harald Welte <laforge@gnumonks.org>
+  *    - new API and handling of conntrack/nat helpers
+@@ -11,11 +12,10 @@
+  * 16 Jul 2002: Harald Welte <laforge@gnumonks.org>
+  *    - add usage/reference counts to ip_conntrack_expect
+  *    - export ip_conntrack[_expect]_{find_get,put} functions
++ * 05 Aug 2002: Harald Welte <laforge@gnumonks.org>
++ *    - added DocBook-style comments for public API
+  * */
+-#ifdef MODULE
+-#define __NO_VERSION__
+-#endif
+ #include <linux/version.h>
+ #include <linux/config.h>
+ #include <linux/types.h>
+@@ -31,6 +31,8 @@
+ #include <linux/stddef.h>
+ #include <linux/sysctl.h>
+ #include <linux/slab.h>
++#include <linux/random.h>
++#include <linux/jhash.h>
+ /* For ERR_PTR().  Yeah, I know... --RR */
+ #include <linux/fs.h>
+@@ -61,10 +63,11 @@
+ LIST_HEAD(protocol_list);
+ static LIST_HEAD(helpers);
+ unsigned int ip_conntrack_htable_size = 0;
+-static int ip_conntrack_max = 0;
++int ip_conntrack_max = 0;
+ static atomic_t ip_conntrack_count = ATOMIC_INIT(0);
+ struct list_head *ip_conntrack_hash;
+ static kmem_cache_t *ip_conntrack_cachep;
++struct ip_conntrack ip_conntrack_untracked;
+ extern struct ip_conntrack_protocol ip_conntrack_generic_protocol;
+@@ -87,6 +90,10 @@
+       return p;
+ }
++/**
++ * ip_ct_find_proto - Find layer 4 protocol helper for given protocol number
++ * @protocol: protocol number
++ */
+ struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol)
+ {
+       struct ip_conntrack_protocol *p;
+@@ -107,20 +114,19 @@
+       nf_conntrack_put(&ct->infos[0]);
+ }
+-static inline u_int32_t
++static int ip_conntrack_hash_rnd_initted;
++static unsigned int ip_conntrack_hash_rnd;
++
++static u_int32_t
+ hash_conntrack(const struct ip_conntrack_tuple *tuple)
+ {
+ #if 0
+       dump_tuple(tuple);
+ #endif
+-      /* ntohl because more differences in low bits. */
+-      /* To ensure that halves of the same connection don't hash
+-         clash, we add the source per-proto again. */
+-      return (ntohl(tuple->src.ip + tuple->dst.ip
+-                   + tuple->src.u.all + tuple->dst.u.all
+-                   + tuple->dst.protonum)
+-              + ntohs(tuple->src.u.all))
+-              % ip_conntrack_htable_size;
++      return (jhash_3words(tuple->src.ip,
++                           (tuple->dst.ip ^ tuple->dst.protonum),
++                           (tuple->src.u.all | (tuple->dst.u.all << 16)),
++                           ip_conntrack_hash_rnd) % ip_conntrack_htable_size);
+ }
+ inline int
+@@ -144,12 +150,22 @@
+       tuple->dst.ip = iph->daddr;
+       tuple->dst.protonum = iph->protocol;
++      tuple->src.u.all = tuple->dst.u.all = 0;
++
+       ret = protocol->pkt_to_tuple((u_int32_t *)iph + iph->ihl,
+                                    len - 4*iph->ihl,
+                                    tuple);
+       return ret;
+ }
++int
++ip_conntrack_get_tuple(const struct iphdr *iph, size_t len,
++                     struct ip_conntrack_tuple *tuple,
++                     struct ip_conntrack_protocol *protocol)
++{
++      return get_tuple(iph, len, tuple, protocol);
++}
++
+ static int
+ invert_tuple(struct ip_conntrack_tuple *inverse,
+            const struct ip_conntrack_tuple *orig,
+@@ -159,6 +175,8 @@
+       inverse->dst.ip = orig->src.ip;
+       inverse->dst.protonum = orig->dst.protonum;
++      inverse->src.u.all = inverse->dst.u.all = 0;
++
+       return protocol->invert_tuple(inverse, orig);
+ }
+@@ -176,8 +194,8 @@
+ static void
+ destroy_expect(struct ip_conntrack_expect *exp)
+ {
+-      DEBUGP("destroy_expect(%p) use=%d\n", exp, atomic_read(exp->use));
+-      IP_NF_ASSERT(atomic_read(exp->use));
++      DEBUGP("destroy_expect(%p) use=%d\n", exp, atomic_read(&exp->use));
++      IP_NF_ASSERT(atomic_read(&exp->use));
+       IP_NF_ASSERT(!timer_pending(&exp->timeout));
+       kfree(exp);
+@@ -259,16 +277,14 @@
+ }
+ /* delete all unconfirmed expectations for this conntrack */
+-static void remove_expectations(struct ip_conntrack *ct)
++static void remove_expectations(struct ip_conntrack *ct, int drop_refcount)
+ {
+       struct list_head *exp_entry, *next;
+       struct ip_conntrack_expect *exp;
+       DEBUGP("remove_expectations(%p)\n", ct);
+-      for (exp_entry = ct->sibling_list.next;
+-           exp_entry != &ct->sibling_list; exp_entry = next) {
+-              next = exp_entry->next;
++      list_for_each_safe(exp_entry, next, &ct->sibling_list) {
+               exp = list_entry(exp_entry, struct ip_conntrack_expect,
+                                expected_list);
+@@ -276,6 +292,11 @@
+                * the un-established ones only */
+               if (exp->sibling) {
+                       DEBUGP("remove_expectations: skipping established %p of %p\n", exp->sibling, ct);
++                      if (drop_refcount) {
++                              /* Indicate that this expectations parent is dead */
++                              ip_conntrack_put(exp->expectant);
++                              exp->expectant = NULL;
++                      }
+                       continue;
+               }
+@@ -290,20 +311,18 @@
+ static void
+ clean_from_lists(struct ip_conntrack *ct)
+ {
++      unsigned int ho, hr;
++      
+       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
+-           doesn't do this. --RR */
+-      LIST_DELETE(&ip_conntrack_hash
+-                  [hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple)],
+-                  &ct->tuplehash[IP_CT_DIR_ORIGINAL]);
+-      LIST_DELETE(&ip_conntrack_hash
+-                  [hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple)],
+-                  &ct->tuplehash[IP_CT_DIR_REPLY]);
++
++      ho = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++      hr = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
++      LIST_DELETE(&ip_conntrack_hash[ho], &ct->tuplehash[IP_CT_DIR_ORIGINAL]);
++      LIST_DELETE(&ip_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]);
+       /* Destroy all un-established, pending expectations */
+-      remove_expectations(ct);
++      remove_expectations(ct, 1);
+ }
+ static void
+@@ -316,9 +335,6 @@
+       IP_NF_ASSERT(atomic_read(&nfct->use) == 0);
+       IP_NF_ASSERT(!timer_pending(&ct->timeout));
+-      if (ct->master && master_ct(ct))
+-              ip_conntrack_put(master_ct(ct));
+-
+       /* To make sure we don't get any weird locking issues here:
+        * destroy_conntrack() MUST NOT be called with a write lock
+        * to ip_conntrack_lock!!! -HW */
+@@ -330,11 +346,17 @@
+               ip_conntrack_destroyed(ct);
+       WRITE_LOCK(&ip_conntrack_lock);
++      /* Delete us from our own list to prevent corruption later */
++      list_del(&ct->sibling_list);
++
+       /* Delete our master expectation */
+       if (ct->master) {
+-              /* can't call __unexpect_related here,
+-               * since it would screw up expect_list */
+-              list_del(&ct->master->expected_list);
++              if (ct->master->expectant) {
++                      /* can't call __unexpect_related here,
++                       * since it would screw up expect_list */
++                      list_del(&ct->master->expected_list);
++                      ip_conntrack_put(ct->master->expectant);
++              }
+               kfree(ct->master);
+       }
+       WRITE_UNLOCK(&ip_conntrack_lock);
+@@ -344,7 +366,7 @@
+       atomic_dec(&ip_conntrack_count);
+ }
+-static void death_by_timeout(unsigned long ul_conntrack)
++void ip_ct_death_by_timeout(unsigned long ul_conntrack)
+ {
+       struct ip_conntrack *ct = (void *)ul_conntrack;
+@@ -369,16 +391,24 @@
+                   const struct ip_conntrack *ignored_conntrack)
+ {
+       struct ip_conntrack_tuple_hash *h;
++      unsigned int hash = hash_conntrack(tuple);
+       MUST_BE_READ_LOCKED(&ip_conntrack_lock);
+-      h = LIST_FIND(&ip_conntrack_hash[hash_conntrack(tuple)],
++      h = LIST_FIND(&ip_conntrack_hash[hash],
+                     conntrack_tuple_cmp,
+                     struct ip_conntrack_tuple_hash *,
+                     tuple, ignored_conntrack);
+       return h;
+ }
+-/* Find a connection corresponding to a tuple. */
++/**
++ * ip_conntrack_find_get - find conntrack according to tuple
++ * @tuple: conntrack tuple for which we search conntrack
++ * @ignored_conntrack: ignore this conntrack during search
++ *
++ * This function increments the reference count of the found
++ * conntrack (if any).
++ */
+ struct ip_conntrack_tuple_hash *
+ ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple,
+                     const struct ip_conntrack *ignored_conntrack)
+@@ -406,7 +436,14 @@
+       return ct;
+ }
+-/* Return conntrack and conntrack_info given skb->nfct->master */
++/**
++ * ip_conntrack_get - Return conntrack and conntrack_info for given skb
++ * @skb: skb for which we want to find conntrack and conntrack_info
++ * @ctinfo: pointer to ctinfo, used as return value
++ *
++ * This function resolves the respective conntrack and conntrack_info
++ * structures for the connection this packet (skb) is part of.
++ */
+ struct ip_conntrack *
+ ip_conntrack_get(struct sk_buff *skb, enum ip_conntrack_info *ctinfo)
+ {
+@@ -467,6 +504,7 @@
+               ct->timeout.expires += jiffies;
+               add_timer(&ct->timeout);
+               atomic_inc(&ct->ct_general.use);
++              set_bit(IPS_CONFIRMED_BIT, &ct->status);
+               WRITE_UNLOCK(&ip_conntrack_lock);
+               return NF_ACCEPT;
+       }
+@@ -475,8 +513,14 @@
+       return NF_DROP;
+ }
+-/* Returns true if a connection correspondings to the tuple (required
+-   for NAT). */
++/**
++ * ip_conntrack_tuple_taken - Find out if tuple is already in use
++ * @tuple: tuple to be used for this test
++ * @ignored_conntrack: conntrack which is excluded from result
++ *
++ * This function is called by the NAT code in order to find out if
++ * a particular tuple is already in use by some connection.
++ */
+ int
+ ip_conntrack_tuple_taken(const struct ip_conntrack_tuple *tuple,
+                        const struct ip_conntrack *ignored_conntrack)
+@@ -585,7 +629,7 @@
+    connection.  Too bad: we're in trouble anyway. */
+ static inline int unreplied(const struct ip_conntrack_tuple_hash *i)
+ {
+-      return !(i->ctrack->status & IPS_ASSURED);
++      return !(test_bit(IPS_ASSURED_BIT, &i->ctrack->status));
+ }
+ static int early_drop(struct list_head *chain)
+@@ -595,7 +639,7 @@
+       int dropped = 0;
+       READ_LOCK(&ip_conntrack_lock);
+-      h = LIST_FIND(chain, unreplied, struct ip_conntrack_tuple_hash *);
++      h = LIST_FIND_B(chain, unreplied, struct ip_conntrack_tuple_hash *);
+       if (h)
+               atomic_inc(&h->ctrack->ct_general.use);
+       READ_UNLOCK(&ip_conntrack_lock);
+@@ -604,7 +648,7 @@
+               return dropped;
+       if (del_timer(&h->ctrack->timeout)) {
+-              death_by_timeout((unsigned long)h->ctrack);
++              ip_ct_death_by_timeout((unsigned long)h->ctrack);
+               dropped = 1;
+       }
+       ip_conntrack_put(h->ctrack);
+@@ -616,7 +660,13 @@
+ {
+       return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask);
+ }
+-
++/**
++ * ip_ct_find_helper - Find application helper according to tuple
++ * @tuple: tuple for which helper needs to be found
++ *
++ * This function is used to determine if any registered conntrack helper
++ * is to be used for the given tuple.
++ */
+ struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple)
+ {
+       return LIST_FIND(&helpers, helper_cmp,
+@@ -633,11 +683,16 @@
+ {
+       struct ip_conntrack *conntrack;
+       struct ip_conntrack_tuple repl_tuple;
+-      size_t hash, repl_hash;
++      size_t hash;
+       struct ip_conntrack_expect *expected;
+       int i;
+       static unsigned int drop_next = 0;
++      if (!ip_conntrack_hash_rnd_initted) {
++              get_random_bytes(&ip_conntrack_hash_rnd, 4);
++              ip_conntrack_hash_rnd_initted = 1;
++      }
++
+       hash = hash_conntrack(tuple);
+       if (ip_conntrack_max &&
+@@ -661,7 +716,6 @@
+               DEBUGP("Can't invert tuple.\n");
+               return NULL;
+       }
+-      repl_hash = hash_conntrack(&repl_tuple);
+       conntrack = kmem_cache_alloc(ip_conntrack_cachep, GFP_ATOMIC);
+       if (!conntrack) {
+@@ -686,13 +740,10 @@
+       /* Don't set timer yet: wait for confirmation */
+       init_timer(&conntrack->timeout);
+       conntrack->timeout.data = (unsigned long)conntrack;
+-      conntrack->timeout.function = death_by_timeout;
++      conntrack->timeout.function = ip_ct_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(&ip_conntrack_lock);
+       /* Need finding and deleting of expected ONLY if we win race */
+       READ_LOCK(&ip_conntrack_expect_tuple_lock);
+@@ -700,6 +751,14 @@
+                            struct ip_conntrack_expect *, tuple);
+       READ_UNLOCK(&ip_conntrack_expect_tuple_lock);
++      /* If master is not in hash table yet (ie. packet hasn't left
++         this machine yet), how can other end know about expected?
++         Hence these are not the droids you are looking for (if
++         master ct never got confirmed, we'd hold a reference to it
++         and weird things would happen to future packets). */
++      if (expected && !is_confirmed(expected->expectant))
++              expected = NULL;
++
+       /* Look up the conntrack helper for master connections only */
+       if (!expected)
+               conntrack->helper = ip_ct_find_helper(&repl_tuple);
+@@ -710,19 +769,17 @@
+           && ! del_timer(&expected->timeout))
+               expected = NULL;
+-      /* 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)) {
++      if (expected) {
+               DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n",
+                       conntrack, expected);
+               /* Welcome, Mr. Bond.  We've been expecting you... */
+               IP_NF_ASSERT(master_ct(conntrack));
+-              conntrack->status = IPS_EXPECTED;
++              __set_bit(IPS_EXPECTED_BIT, &conntrack->status);
+               conntrack->master = expected;
+               expected->sibling = conntrack;
++#if CONFIG_IP_NF_CONNTRACK_MARK
++              conntrack->mark = expected->expectant->mark;
++#endif
+               LIST_DELETE(&ip_conntrack_expect_list, expected);
+               expected->expectant->expecting--;
+               nf_conntrack_get(&master_ct(conntrack)->infos[0]);
+@@ -768,11 +825,11 @@
+               *set_reply = 1;
+       } else {
+               /* Once we've had two way comms, always ESTABLISHED. */
+-              if (h->ctrack->status & IPS_SEEN_REPLY) {
++              if (test_bit(IPS_SEEN_REPLY_BIT, &h->ctrack->status)) {
+                       DEBUGP("ip_conntrack_in: normal packet for %p\n",
+                              h->ctrack);
+                       *ctinfo = IP_CT_ESTABLISHED;
+-              } else if (h->ctrack->status & IPS_EXPECTED) {
++              } else if (test_bit(IPS_EXPECTED_BIT, &h->ctrack->status)) {
+                       DEBUGP("ip_conntrack_in: related packet for %p\n",
+                              h->ctrack);
+                       *ctinfo = IP_CT_RELATED;
+@@ -907,6 +964,14 @@
+       return ip_ct_tuple_mask_cmp(&i->tuple, tuple, &intersect_mask);
+ }
++/**
++ * ip_conntrack_unexpect_related - Unexpect a related connection
++ * @expect: expecattin to be removed
++ *
++ * This function removes an existing expectation, that has not yet been
++ * confirmed (i.e. expectation was issued, but expected connection didn't
++ * arrive yet)
++ */
+ inline void ip_conntrack_unexpect_related(struct ip_conntrack_expect *expect)
+ {
+       WRITE_LOCK(&ip_conntrack_lock);
+@@ -924,7 +989,20 @@
+       WRITE_UNLOCK(&ip_conntrack_lock);
+ }
+-/* Add a related connection. */
++/**
++ * ip_conntrack_expect_related - Expect a related connection
++ * @related_to: master conntrack
++ * @expect: expectation with all values filled in
++ *
++ * This function is called by conntrack application helpers who
++ * have detected that the control (master) connection is just about
++ * to negotiate a related slave connection. 
++ *
++ * Note: This function allocates it's own struct ip_conntrack_expect,
++ * copying the values from the 'expect' parameter.  Thus, 'expect' can
++ * be allocated on the stack and does not need to be valid after this
++ * function returns.
++ */
+ int ip_conntrack_expect_related(struct ip_conntrack *related_to,
+                               struct ip_conntrack_expect *expect)
+ {
+@@ -936,8 +1014,8 @@
+        * so there is no need to use the tuple lock too */
+       DEBUGP("ip_conntrack_expect_related %p\n", related_to);
+-      DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple);
+-      DEBUGP("mask:  "); DUMP_TUPLE(&expect->mask);
++      DEBUGP("tuple: "); DUMP_TUPLE_RAW(&expect->tuple);
++      DEBUGP("mask:  "); DUMP_TUPLE_RAW(&expect->mask);
+       old = LIST_FIND(&ip_conntrack_expect_list, resent_expect,
+                       struct ip_conntrack_expect *, &expect->tuple, 
+@@ -966,23 +1044,28 @@
+                  related_to->expecting >= related_to->helper->max_expected) {
+               struct list_head *cur_item;
+               /* old == NULL */
+-              if (net_ratelimit())
+-                      printk(KERN_WARNING 
+-                             "ip_conntrack: max number of expected "
+-                             "connections %i of %s reached for "
+-                             "%u.%u.%u.%u->%u.%u.%u.%u%s\n",
+-                             related_to->helper->max_expected, 
+-                             related_to->helper->name,
+-                             NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
+-                             NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip),
+-                             related_to->helper->flags & IP_CT_HELPER_F_REUSE_EXPECT ?
+-                             ", reusing" : "");
+               if (!(related_to->helper->flags & 
+                     IP_CT_HELPER_F_REUSE_EXPECT)) {
+                       WRITE_UNLOCK(&ip_conntrack_lock);
++                      if (net_ratelimit())
++                              printk(KERN_WARNING
++                                     "ip_conntrack: max number of expected "
++                                     "connections %i of %s reached for "
++                                     "%u.%u.%u.%u->%u.%u.%u.%u\n",
++                                     related_to->helper->max_expected,
++                                     related_to->helper->name,
++                                     NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
++                                     NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip));
+                       return -EPERM;
+               }
+-
++              DEBUGP("ip_conntrack: max number of expected "
++                     "connections %i of %s reached for "
++                     "%u.%u.%u.%u->%u.%u.%u.%u, reusing\n",
++                     related_to->helper->max_expected,
++                     related_to->helper->name,
++                     NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
++                     NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip));
++ 
+               /* choose the the oldest expectation to evict */
+               list_for_each(cur_item, &related_to->sibling_list) { 
+                       struct ip_conntrack_expect *cur;
+@@ -1023,18 +1106,11 @@
+               return -ENOMEM;
+       }
+       
+-      /* Zero out the new structure, then fill out it with the data */
+       DEBUGP("new expectation %p of conntrack %p\n", new, related_to);
+-      memset(new, 0, sizeof(*expect));
+-      INIT_LIST_HEAD(&new->list);
+-      INIT_LIST_HEAD(&new->expected_list);
+       memcpy(new, expect, sizeof(*expect));
+       new->expectant = related_to;
+       new->sibling = NULL;
+-      /* increase usage count. This sucks. The memset above overwrites
+-       * old usage count [if still present] and we increase to one.  Only
+-       * works because everything is done under ip_conntrack_lock() */
+-      atomic_inc(&new->use);
++      atomic_set(&new->use, 1);
+       
+       /* add to expected list for this connection */  
+       list_add(&new->expected_list, &related_to->sibling_list);
+@@ -1056,7 +1132,15 @@
+       return ret;
+ }
+-/* Change tuple in an existing expectation */
++/**
++ * ip_conntrack_change_expect - Change tuple in existing expectation
++ * @expect: expectation which is to be changed
++ * @newtuple: new tuple for expect
++ *
++ * This function is mostly called by NAT application helpers, who want to
++ * change an expectation issued by their respective conntrack application
++ * helper counterpart.
++ */
+ int ip_conntrack_change_expect(struct ip_conntrack_expect *expect,
+                              struct ip_conntrack_tuple *newtuple)
+ {
+@@ -1064,15 +1148,14 @@
+       MUST_BE_READ_LOCKED(&ip_conntrack_lock);
+       WRITE_LOCK(&ip_conntrack_expect_tuple_lock);
+-
+       DEBUGP("change_expect:\n");
+-      DEBUGP("exp tuple: "); DUMP_TUPLE(&expect->tuple);
+-      DEBUGP("exp mask:  "); DUMP_TUPLE(&expect->mask);
+-      DEBUGP("newtuple:  "); DUMP_TUPLE(newtuple);
++      DEBUGP("exp tuple: "); DUMP_TUPLE_RAW(&expect->tuple);
++      DEBUGP("exp mask:  "); DUMP_TUPLE_RAW(&expect->mask);
++      DEBUGP("newtuple:  "); DUMP_TUPLE_RAW(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) 
++              if (!ip_ct_tuple_mask_cmp(&expect->tuple, newtuple, &expect->mask)
+                   && LIST_FIND(&ip_conntrack_expect_list, expect_clash,
+                                struct ip_conntrack_expect *, newtuple, &expect->mask)) {
+                       /* Force NAT to find an unused tuple */
+@@ -1097,8 +1180,15 @@
+       return ret;
+ }
+-/* Alter reply tuple (maybe alter helper).  If it's already taken,
+-   return 0 and don't do alteration. */
++/**
++ * ip_conntrack_alter_reply - Alter reply tuple of conntrack
++ * @conntrack: conntrack whose reply tuple we want to alter
++ * @newreply: designated reply tuple for this conntrack
++ *
++ * This function alters the reply tuple of a conntrack to the given
++ * newreply tuple.  If this newreply tuple is already taken, return 0
++ * and don't do alteration
++ */
+ int ip_conntrack_alter_reply(struct ip_conntrack *conntrack,
+                            const struct ip_conntrack_tuple *newreply)
+ {
+@@ -1123,6 +1213,13 @@
+       return 1;
+ }
++/**
++ * ip_conntrack_helper_register - Register a conntrack application helper
++ * @me: structure describing the helper
++ *
++ * This function is called by conntrack application helpers to register
++ * themselves with the conntrack core.
++ */
+ int ip_conntrack_helper_register(struct ip_conntrack_helper *me)
+ {
+       MOD_INC_USE_COUNT;
+@@ -1139,13 +1236,20 @@
+ {
+       if (i->ctrack->helper == me) {
+               /* Get rid of any expected. */
+-              remove_expectations(i->ctrack);
++              remove_expectations(i->ctrack, 0);
+               /* And *then* set helper to NULL */
+               i->ctrack->helper = NULL;
+       }
+       return 0;
+ }
++/**
++ * ip_conntrack_helper_unregister - Unregister a conntrack application helper
++ * @me: structure describing the helper
++ *
++ * This function is called by conntrack application helpers to unregister
++ * themselvers from the conntrack core.
++ */
+ void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me)
+ {
+       unsigned int i;
+@@ -1167,7 +1271,14 @@
+       MOD_DEC_USE_COUNT;
+ }
+-/* Refresh conntrack for this many jiffies. */
++/**
++ * ip_ct_refresh - Refresh conntrack timer for given conntrack
++ * @ct: conntrack which we want to refresh
++ * @extra_jiffies: number of jiffies to add
++ *
++ * This function is called by protocol helpers and application helpers in
++ * order to change the expiration timer of a conntrack entry.
++ */
+ void ip_ct_refresh(struct ip_conntrack *ct, unsigned long extra_jiffies)
+ {
+       IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct);
+@@ -1177,8 +1288,10 @@
+       if (!is_confirmed(ct))
+               ct->timeout.expires = extra_jiffies;
+       else {
+-              /* Need del_timer for race avoidance (may already be dying). */
+-              if (del_timer(&ct->timeout)) {
++              /* Don't update timer for each packet, only if it's been >HZ
++               * ticks since last update.
++               * Need del_timer for race avoidance (may already be dying). */
++              if (abs(jiffies + extra_jiffies - ct->timeout.expires) >= HZ && del_timer(&ct->timeout)) {
+                       ct->timeout.expires = jiffies + extra_jiffies;
+                       add_timer(&ct->timeout);
+               }
+@@ -1186,7 +1299,16 @@
+       WRITE_UNLOCK(&ip_conntrack_lock);
+ }
+-/* Returns new sk_buff, or NULL */
++
++/**
++ * ip_ct_gather_frags - Gather fragments of a particular skb
++ * @skb: pointer to sk_buff of fragmented IP packet
++ *
++ * This code is just a wrapper around the defragmentation code in the core IPv4
++ * stack.  It also takes care of nonlinear skb's.
++ *
++ * Returns new sk_buff, or NULL
++ */
+ struct sk_buff *
+ ip_ct_gather_frags(struct sk_buff *skb)
+ {
+@@ -1274,6 +1396,16 @@
+       return h;
+ }
++/**
++ * ip_ct_selective_cleanup - Selectively delete a set of conntrack entries
++ * @kill: callback function selecting which entries to delete
++ * @data: opaque data pointer, becomes 2nd argument for kill function
++ *
++ * This function can be used to selectively delete elements of the conntrack
++ * hashtable.  The function iterates over the list of conntrack entries and
++ * calls the 'kill' function for every entry.  If the return value is true,
++ * the connection is deleted (death_by_timeout).
++ */
+ void
+ ip_ct_selective_cleanup(int (*kill)(const struct ip_conntrack *i, void *data),
+                       void *data)
+@@ -1284,7 +1416,7 @@
+       while ((h = get_next_corpse(kill, data)) != NULL) {
+               /* Time to push up daises... */
+               if (del_timer(&h->ctrack->timeout))
+-                      death_by_timeout((unsigned long)h->ctrack);
++                      ip_ct_death_by_timeout((unsigned long)h->ctrack);
+               /* ... else the timer will get him soon. */
+               ip_conntrack_put(h->ctrack);
+@@ -1299,9 +1431,14 @@
+ getorigdst(struct sock *sk, int optval, void *user, int *len)
+ {
+       struct ip_conntrack_tuple_hash *h;
+-      struct ip_conntrack_tuple tuple = { { sk->rcv_saddr, { sk->sport } },
+-                                          { sk->daddr, { sk->dport },
+-                                            IPPROTO_TCP } };
++      struct ip_conntrack_tuple tuple;
++
++      IP_CT_TUPLE_BLANK(&tuple);
++      tuple.src.ip = sk->rcv_saddr;
++      tuple.src.u.tcp.port = sk->sport;
++      tuple.dst.ip = sk->daddr;
++      tuple.dst.u.tcp.port = sk->dport;
++      tuple.dst.protonum = IPPROTO_TCP;
+       /* We only do TCP at the moment: is there a better way? */
+       if (strcmp(sk->prot->name, "TCP") != 0) {
+@@ -1345,29 +1482,6 @@
+     SO_ORIGINAL_DST, SO_ORIGINAL_DST+1, &getorigdst,
+     0, NULL };
+-#define NET_IP_CONNTRACK_MAX 2089
+-#define NET_IP_CONNTRACK_MAX_NAME "ip_conntrack_max"
+-
+-#ifdef CONFIG_SYSCTL
+-static struct ctl_table_header *ip_conntrack_sysctl_header;
+-
+-static ctl_table ip_conntrack_table[] = {
+-      { NET_IP_CONNTRACK_MAX, NET_IP_CONNTRACK_MAX_NAME, &ip_conntrack_max,
+-        sizeof(ip_conntrack_max), 0644,  NULL, proc_dointvec },
+-      { 0 }
+-};
+-
+-static ctl_table ip_conntrack_dir_table[] = {
+-      {NET_IPV4, "ipv4", NULL, 0, 0555, ip_conntrack_table, 0, 0, 0, 0, 0},
+-      { 0 }
+-};
+-
+-static ctl_table ip_conntrack_root_table[] = {
+-      {CTL_NET, "net", NULL, 0, 0555, ip_conntrack_dir_table, 0, 0, 0, 0, 0},
+-      { 0 }
+-};
+-#endif /*CONFIG_SYSCTL*/
+-
+ static int kill_all(const struct ip_conntrack *i, void *data)
+ {
+       return 1;
+@@ -1377,9 +1491,6 @@
+    supposed to kill the mall. */
+ void ip_conntrack_cleanup(void)
+ {
+-#ifdef CONFIG_SYSCTL
+-      unregister_sysctl_table(ip_conntrack_sysctl_header);
+-#endif
+       ip_ct_attach = NULL;
+       /* This makes sure all current packets have passed through
+            netfilter framework.  Roll on, two-stage module
+@@ -1423,7 +1534,7 @@
+       ip_conntrack_max = 8 * ip_conntrack_htable_size;
+       printk("ip_conntrack version %s (%u buckets, %d max)"
+-             " - %d bytes per conntrack\n", IP_CONNTRACK_VERSION,
++             " - %Zd bytes per conntrack\n", IP_CONNTRACK_VERSION,
+              ip_conntrack_htable_size, ip_conntrack_max,
+              sizeof(struct ip_conntrack));
+@@ -1458,23 +1569,19 @@
+       for (i = 0; i < ip_conntrack_htable_size; i++)
+               INIT_LIST_HEAD(&ip_conntrack_hash[i]);
+-/* This is fucking braindead.  There is NO WAY of doing this without
+-   the CONFIG_SYSCTL unless you don't want to detect errors.
+-   Grrr... --RR */
+-#ifdef CONFIG_SYSCTL
+-      ip_conntrack_sysctl_header
+-              = register_sysctl_table(ip_conntrack_root_table, 0);
+-      if (ip_conntrack_sysctl_header == NULL) {
+-              goto err_free_ct_cachep;
+-      }
+-#endif /*CONFIG_SYSCTL*/
+-
+       /* For use by ipt_REJECT */
+       ip_ct_attach = ip_conntrack_attach;
++      
++      /* Set up fake conntrack:
++          - to never be deleted, not in any hashes */
++      atomic_set(&ip_conntrack_untracked.ct_general.use, 1);
++      /*  - and look it like as a confirmed connection */
++      set_bit(IPS_CONFIRMED_BIT, &ip_conntrack_untracked.status);
++      /*  - and prepare the ctinfo field for NAT. */
++      ip_conntrack_untracked.infos[IP_CT_NEW].master = &ip_conntrack_untracked.ct_general;
++      
+       return ret;
+-err_free_ct_cachep:
+-      kmem_cache_destroy(ip_conntrack_cachep);
+ err_free_hash:
+       vfree(ip_conntrack_hash);
+ err_unreg_sockopt:
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_egg.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_egg.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_egg.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_egg.c Wed Sep 24 09:17:40 2003
+@@ -0,0 +1,237 @@
++/* Eggdrop extension for IP connection tracking, Version 0.0.5
++ * based on ip_conntrack_irc.c        
++ *
++ *      This module only supports the share userfile-send command,
++ *      used by eggdrops to share it's userfile.
++ *
++ *      There are no support for NAT at the moment.
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ *    Module load syntax:
++ *    
++ *    please give the ports of all Eggdrops You have running
++ *      on your system, the default port is 3333.
++ *
++ *      2001-04-19: Security update. IP addresses are now compared
++ *                  to prevent unauthorized "related" access.
++ *
++ *      2002-03-25: Harald Welte <laforge@gnumonks.org>:
++ *                Port to netfilter 'newnat' API.
++ */
++
++#include <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++#include <net/tcp.h>
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++
++#define MAX_PORTS 8
++static int ports[MAX_PORTS];
++static int ports_c = 0;
++static unsigned int egg_timeout = 300;
++
++MODULE_AUTHOR("Magnus Sandin <magnus@sandin.cx>");
++MODULE_DESCRIPTION("Eggdrop (userfile-sharing) connection tracking module");
++MODULE_LICENSE("GPL");
++#ifdef MODULE_PARM
++MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
++MODULE_PARM_DESC(ports, "port numbers of eggdrop servers");
++#endif
++
++DECLARE_LOCK(ip_egg_lock);
++struct module *ip_conntrack_egg = THIS_MODULE;
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++int parse_command(char *data, char *data_end, u_int32_t * ip, u_int16_t * port)
++/* tries to get the ip_addr and port out of a eggdrop command
++   return value: -1 on failure, 0 on success 
++   data               pointer to first byte of DCC command data
++   data_end   pointer to last byte of dcc command data
++   ip         returns parsed ip of dcc command
++   port               returns parsed port of dcc command */
++{
++      if (data > data_end)
++              return -1;
++      
++      *ip = simple_strtoul(data, &data, 10);
++
++      /* skip blanks between ip and port */
++      while (*data == ' ' && data < data_end)
++              data++;
++
++      *port = simple_strtoul(data, &data, 10);
++      return 0;
++}
++
++
++static int help(const struct iphdr *iph, size_t len,
++              struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
++{
++      /* tcplen not negative guarenteed by ip_conntrack_tcp.c */
++      struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
++      char *data = (char *) tcph + tcph->doff * 4;
++      char *data_limit;
++      u_int32_t tcplen = len - iph->ihl * 4;
++      u_int32_t datalen = tcplen - tcph->doff * 4;
++      int dir = CTINFO2DIR(ctinfo);
++      int bytes_scanned = 0;
++      struct ip_conntrack_expect exp;
++
++      u_int32_t egg_ip;
++      u_int16_t egg_port;
++
++      DEBUGP("entered\n");
++
++      /* If packet is coming from IRC server */
++      if (dir != IP_CT_DIR_REPLY)
++              return NF_ACCEPT;
++
++      /* Until there's been traffic both ways, don't look in packets. */
++      if (ctinfo != IP_CT_ESTABLISHED
++          && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
++              DEBUGP("Conntrackinfo = %u\n", ctinfo);
++              return NF_ACCEPT;
++      }
++
++      /* Not whole TCP header? */
++      if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
++              DEBUGP("tcplen = %u\n", (unsigned) tcplen);
++              return NF_ACCEPT;
++      }
++
++      /* Checksum invalid?  Ignore. */
++      /* FIXME: Source route IP option packets --RR */
++      if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
++                       csum_partial((char *) tcph, tcplen, 0))) {
++              DEBUGP("bad csum: %p %u %u.%u.%u.%u -> %u.%u.%u.%u\n",
++                      tcph, tcplen, NIPQUAD(iph->saddr),
++                      NIPQUAD(iph->daddr));
++              return NF_ACCEPT;
++      }
++
++      data_limit = (char *) data + datalen;
++      while (datalen > 5 && bytes_scanned < 128) {
++              if (memcmp(data, "s us ", 5)) {
++                      data++;
++                      datalen--;
++                      bytes_scanned++;
++                      continue;
++              }
++
++              data += 5;
++
++              DEBUGP("Userfile-share found in connection "
++                      "%u.%u.%u.%u -> %u.%u.%u.%u\n",
++                      NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++
++              if (parse_command((char *) data, data_limit, &egg_ip,
++                                &egg_port)) {
++                      DEBUGP("no data in userfile-share pkt\n");
++                      return NF_ACCEPT;
++              }
++
++              memset(&exp, 0, sizeof(exp));
++
++              if (ct->tuplehash[dir].tuple.src.ip != htonl(egg_ip)) {
++                      if (net_ratelimit())
++                              printk("Forged Eggdrop command from "
++                                     "%u.%u.%u.%u: %u.%u.%u.%u:%u\n",
++                                     NIPQUAD(ct->tuplehash[dir].tuple.src.ip),
++                                     HIPQUAD(egg_ip), egg_port);
++                      return NF_ACCEPT;
++              }
++
++              exp.tuple.src.ip = iph->daddr;
++              exp.tuple.src.u.tcp.port = 0;
++              exp.tuple.dst.ip = htonl(egg_ip);
++              exp.tuple.dst.u.tcp.port = htons(egg_port);
++              exp.tuple.dst.protonum = IPPROTO_TCP;
++
++              exp.mask.dst.u.tcp.port = 0xffff;
++              exp.mask.dst.protonum = 0xffff;
++
++              DEBUGP("expect_related %u.%u.%u.%u:%u - %u.%u.%u.%u:%u\n",
++                      NIPQUAD(t.src.ip), ntohs(t.src.u.tcp.port),
++                      NIPQUAD(t.dst.ip), ntohs(t.dst.u.tcp.port));
++
++              ip_conntrack_expect_related(ct, &exp);
++              break;
++      }
++      return NF_ACCEPT;
++}
++
++static struct ip_conntrack_helper egg_helpers[MAX_PORTS];
++static char egg_names[MAX_PORTS][14]; /* eggdrop-65535 */
++
++static void deregister_helpers(void) {
++      int i;
++      
++      for (i = 0; i < ports_c; i++) {
++              DEBUGP("unregistering helper for port %d\n", ports[i]);
++              ip_conntrack_helper_unregister(&egg_helpers[i]);
++      }
++}
++
++static int __init init(void)
++{
++      int i, ret;
++      char *tmpname;
++
++      /* If no port given, default to standard eggdrop port */
++      if (ports[0] == 0)
++              ports[0] = 3333;
++
++      for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
++              memset(&egg_helpers[i], 0,
++                     sizeof(struct ip_conntrack_helper));
++              egg_helpers[i].tuple.src.u.tcp.port = htons(ports[i]);
++              egg_helpers[i].tuple.dst.protonum = IPPROTO_TCP;
++              egg_helpers[i].mask.src.u.tcp.port = 0xFFFF;
++              egg_helpers[i].mask.dst.protonum = 0xFFFF;
++              egg_helpers[i].max_expected = 1;
++              egg_helpers[i].timeout = egg_timeout;
++              egg_helpers[i].flags = IP_CT_HELPER_F_REUSE_EXPECT;
++              egg_helpers[i].me = THIS_MODULE;
++              egg_helpers[i].help = help;
++
++              tmpname = &egg_names[i][0];
++              if (ports[i] == 3333)
++                      sprintf(tmpname, "eggdrop");
++              else
++                      sprintf(tmpname, "eggdrop-%d", ports[i]);
++              egg_helpers[i].name = tmpname;
++
++              DEBUGP("port #%d: %d\n", i, ports[i]);
++
++              ret = ip_conntrack_helper_register(&egg_helpers[i]);
++
++              if (ret) {
++                      printk("ip_conntrack_egg: ERROR registering helper "
++                              "for port %d\n", ports[i]);
++                      deregister_helpers();
++                      return 1;
++              }
++              ports_c++;
++      }
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      deregister_helpers();
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_ftp.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_ftp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_ftp.c     Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_ftp.c Wed Sep 24 09:16:17 2003
+@@ -32,7 +32,7 @@
+ static int try_rfc959(const char *, size_t, u_int32_t [], char);
+ static int try_eprt(const char *, size_t, u_int32_t [], char);
+-static int try_espv_response(const char *, size_t, u_int32_t [], char);
++static int try_epsv_response(const char *, size_t, u_int32_t [], char);
+ static struct ftp_search {
+       enum ip_conntrack_dir dir;
+@@ -65,7 +65,7 @@
+               IP_CT_DIR_REPLY,
+               "229 ", sizeof("229 ") - 1, '(', ')',
+               IP_CT_FTP_EPSV,
+-              try_espv_response,
++              try_epsv_response,
+       },
+ };
+@@ -157,7 +157,7 @@
+ }
+ /* Returns 0, or length of numbers: |||6446| */
+-static int try_espv_response(const char *data, size_t dlen, u_int32_t array[6],
++static int try_epsv_response(const char *data, size_t dlen, u_int32_t array[6],
+                            char term)
+ {
+       char delim;
+@@ -200,9 +200,9 @@
+               DEBUGP("ftp: string mismatch\n");
+               for (i = 0; i < plen; i++) {
+-                      DEBUGFTP("ftp:char %u `%c'(%u) vs `%c'(%u)\n",
+-                               i, data[i], data[i],
+-                               pattern[i], pattern[i]);
++                      DEBUGP("ftp:char %u `%c'(%u) vs `%c'(%u)\n",
++                              i, data[i], data[i],
++                              pattern[i], pattern[i]);
+               }
+ #endif
+               return 0;
+@@ -366,11 +366,11 @@
+                   { 0 } },
+                 { htonl((array[0] << 24) | (array[1] << 16)
+                         | (array[2] << 8) | array[3]),
+-                  { htons(array[4] << 8 | array[5]) },
++                  { .tcp = { htons(array[4] << 8 | array[5]) } },
+                   IPPROTO_TCP }});
+       exp->mask = ((struct ip_conntrack_tuple)
+               { { 0xFFFFFFFF, { 0 } },
+-                { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
++                { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
+       exp->expectfn = NULL;
+@@ -405,7 +405,6 @@
+               ports[0] = FTP_PORT;
+       for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+-              memset(&ftp[i], 0, sizeof(struct ip_conntrack_helper));
+               ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
+               ftp[i].tuple.dst.protonum = IPPROTO_TCP;
+               ftp[i].mask.src.u.tcp.port = 0xFFFF;
+@@ -436,9 +435,7 @@
+       return 0;
+ }
+-#ifdef CONFIG_IP_NF_NAT_NEEDED
+ EXPORT_SYMBOL(ip_ftp_lock);
+-#endif
+ MODULE_LICENSE("GPL");
+ module_init(init);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_h323.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_h323.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_h323.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_h323.c        Wed Sep 24 09:17:43 2003
+@@ -0,0 +1,308 @@
++/* 
++ * H.323 'brute force' extension for H.323 connection tracking. 
++ * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
++ *
++ * 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 <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++#include <net/tcp.h>
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++#include <linux/netfilter_ipv4/ip_conntrack_core.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
++#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
++
++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
++MODULE_DESCRIPTION("H.323 'brute force' connection tracking module");
++MODULE_LICENSE("GPL");
++
++DECLARE_LOCK(ip_h323_lock);
++struct module *ip_conntrack_h323 = THIS_MODULE;
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++/* FIXME: This should be in userspace.  Later. */
++static int h245_help(const struct iphdr *iph, size_t len,
++                   struct ip_conntrack *ct,
++                   enum ip_conntrack_info ctinfo)
++{
++      struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
++      unsigned char *data = (unsigned char *) tcph + tcph->doff * 4;
++      unsigned char *data_limit;
++      u_int32_t tcplen = len - iph->ihl * 4;
++      u_int32_t datalen = tcplen - tcph->doff * 4;
++      int dir = CTINFO2DIR(ctinfo);
++      struct ip_ct_h225_master *info = &ct->help.ct_h225_info;
++      struct ip_conntrack_expect expect, *exp = &expect;
++      struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info;
++      u_int16_t data_port;
++      u_int32_t data_ip;
++      unsigned int i;
++
++      DEBUGP("ct_h245_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
++              NIPQUAD(iph->saddr), ntohs(tcph->source),
++              NIPQUAD(iph->daddr), ntohs(tcph->dest));
++
++      /* Can't track connections formed before we registered */
++      if (!info)
++              return NF_ACCEPT;
++              
++      /* Until there's been traffic both ways, don't look in packets. */
++      if (ctinfo != IP_CT_ESTABLISHED
++          && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
++              DEBUGP("ct_h245_help: Conntrackinfo = %u\n", ctinfo);
++              return NF_ACCEPT;
++      }
++
++      /* Not whole TCP header or too short packet? */
++      if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) {
++              DEBUGP("ct_h245_help: tcplen = %u\n", (unsigned)tcplen);
++              return NF_ACCEPT;
++      }
++
++      /* Checksum invalid?  Ignore. */
++      /* FIXME: Source route IP option packets --RR */
++      if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
++                            csum_partial((char *)tcph, tcplen, 0))) {
++              DEBUGP("ct_h245_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
++                     tcph, tcplen, NIPQUAD(iph->saddr),
++                     NIPQUAD(iph->daddr));
++              return NF_ACCEPT;
++      }
++
++      data_limit = (unsigned char *) data + datalen;
++      /* bytes: 0123   45
++                ipadrr port */
++      for (i = 0; data < (data_limit - 5); data++, i++) {
++              data_ip = *((u_int32_t *)data);
++              if (data_ip == iph->saddr) {
++                      data_port = *((u_int16_t *)(data + 4));
++                      memset(&expect, 0, sizeof(expect));
++                      /* update the H.225 info */
++                      DEBUGP("ct_h245_help: new RTCP/RTP requested %u.%u.%u.%u:->%u.%u.%u.%u:%u\n",
++                              NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
++                              NIPQUAD(iph->saddr), ntohs(data_port));
++                      LOCK_BH(&ip_h323_lock);
++                      info->is_h225 = H225_PORT + 1;
++                      exp_info->port = data_port;
++                      exp_info->dir = dir;
++                      exp_info->offset = i;
++
++                      exp->seq = ntohl(tcph->seq) + i;
++                  
++                      exp->tuple = ((struct ip_conntrack_tuple)
++                              { { ct->tuplehash[!dir].tuple.src.ip,
++                                  { 0 } },
++                                { data_ip,
++                                  { .tcp = { data_port } },
++                                  IPPROTO_UDP }});
++                      exp->mask = ((struct ip_conntrack_tuple)
++                              { { 0xFFFFFFFF, { 0 } },
++                                { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
++      
++                      exp->expectfn = NULL;
++                      
++                      /* Ignore failure; should only happen with NAT */
++                      ip_conntrack_expect_related(ct, exp);
++
++                      UNLOCK_BH(&ip_h323_lock);
++              }
++      }
++
++      return NF_ACCEPT;
++
++}
++
++/* H.245 helper is not registered! */
++static struct ip_conntrack_helper h245 = 
++      { { NULL, NULL },
++          "H.245",                            /* name */
++          IP_CT_HELPER_F_REUSE_EXPECT,                /* flags */
++          NULL,                                       /* module */
++          8,                                  /* max_ expected */
++          240,                                        /* timeout */
++          { { 0, { 0 } },                     /* tuple */
++            { 0, { 0 }, IPPROTO_TCP } },
++          { { 0, { 0xFFFF } },                        /* mask */
++            { 0, { 0 }, 0xFFFF } },
++          h245_help                           /* helper */
++      };
++
++static int h225_expect(struct ip_conntrack *ct)
++{
++      WRITE_LOCK(&ip_conntrack_lock);
++      ct->helper = &h245;
++      DEBUGP("h225_expect: helper for %p added\n", ct);
++      WRITE_UNLOCK(&ip_conntrack_lock);
++      
++      return NF_ACCEPT;       /* unused */
++}
++
++/* FIXME: This should be in userspace.  Later. */
++static int h225_help(const struct iphdr *iph, size_t len,
++                   struct ip_conntrack *ct,
++                   enum ip_conntrack_info ctinfo)
++{
++      struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
++      unsigned char *data = (unsigned char *) tcph + tcph->doff * 4;
++      unsigned char *data_limit;
++      u_int32_t tcplen = len - iph->ihl * 4;
++      u_int32_t datalen = tcplen - tcph->doff * 4;
++      int dir = CTINFO2DIR(ctinfo);
++      struct ip_ct_h225_master *info = &ct->help.ct_h225_info;
++      struct ip_conntrack_expect expect, *exp = &expect;
++      struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info;
++      u_int16_t data_port;
++      u_int32_t data_ip;
++      unsigned int i;
++      
++      DEBUGP("ct_h225_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
++              NIPQUAD(iph->saddr), ntohs(tcph->source),
++              NIPQUAD(iph->daddr), ntohs(tcph->dest));
++
++      /* Can't track connections formed before we registered */
++      if (!info)
++              return NF_ACCEPT;
++
++      /* Until there's been traffic both ways, don't look in packets. */
++      if (ctinfo != IP_CT_ESTABLISHED
++          && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
++              DEBUGP("ct_h225_help: Conntrackinfo = %u\n", ctinfo);
++              return NF_ACCEPT;
++      }
++
++      /* Not whole TCP header or too short packet? */
++      if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) {
++              DEBUGP("ct_h225_help: tcplen = %u\n", (unsigned)tcplen);
++              return NF_ACCEPT;
++      }
++
++      /* Checksum invalid?  Ignore. */
++      /* FIXME: Source route IP option packets --RR */
++      if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
++                            csum_partial((char *)tcph, tcplen, 0))) {
++              DEBUGP("ct_h225_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
++                     tcph, tcplen, NIPQUAD(iph->saddr),
++                     NIPQUAD(iph->daddr));
++              return NF_ACCEPT;
++      }
++      
++      data_limit = (unsigned char *) data + datalen;
++      /* bytes: 0123   45
++                ipadrr port */
++      for (i = 0; data < (data_limit - 5); data++, i++) {
++              data_ip = *((u_int32_t *)data);
++              if (data_ip == iph->saddr) {
++                      data_port = *((u_int16_t *)(data + 4));
++                      if (data_port == tcph->source) {
++                              /* Signal address */
++                              DEBUGP("ct_h225_help: sourceCallSignalAddress from %u.%u.%u.%u\n",
++                                      NIPQUAD(iph->saddr));
++                              /* Update the H.225 info so that NAT can mangle the address/port
++                                 even when we have no expected connection! */
++#ifdef CONFIG_IP_NF_NAT_NEEDED
++                              LOCK_BH(&ip_h323_lock);
++                              info->dir = dir;
++                              info->seq[IP_CT_DIR_ORIGINAL] = ntohl(tcph->seq) + i;
++                              info->offset[IP_CT_DIR_ORIGINAL] = i;
++                              UNLOCK_BH(&ip_h323_lock);
++#endif
++                      } else {
++                              memset(&expect, 0, sizeof(expect));
++
++                              /* update the H.225 info */
++                              LOCK_BH(&ip_h323_lock);
++                              info->is_h225 = H225_PORT;
++                              exp_info->port = data_port;
++                              exp_info->dir = dir;
++                              exp_info->offset = i;
++
++                              exp->seq = ntohl(tcph->seq) + i;
++
++                              exp->tuple = ((struct ip_conntrack_tuple)
++                                      { { ct->tuplehash[!dir].tuple.src.ip,
++                                          { 0 } },
++                                        { data_ip,
++                                          { .tcp = { data_port } },
++                                          IPPROTO_TCP }});
++                              exp->mask = ((struct ip_conntrack_tuple)
++                                      { { 0xFFFFFFFF, { 0 } },
++                                        { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
++      
++                              exp->expectfn = h225_expect;
++                              
++                              /* Ignore failure */
++                              ip_conntrack_expect_related(ct, exp);
++
++                              DEBUGP("ct_h225_help: new H.245 requested %u.%u.%u.%u->%u.%u.%u.%u:%u\n",
++                                      NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
++                                      NIPQUAD(iph->saddr), ntohs(data_port));
++
++                              UNLOCK_BH(&ip_h323_lock);
++                      }  
++#ifdef CONFIG_IP_NF_NAT_NEEDED
++              } else if (data_ip == iph->daddr) {
++                      data_port = *((u_int16_t *)(data + 4));
++                      if (data_port == tcph->dest) {
++                              /* Signal address */
++                              DEBUGP("ct_h225_help: destCallSignalAddress %u.%u.%u.%u\n",
++                                      NIPQUAD(iph->daddr));
++                              /* Update the H.225 info so that NAT can mangle the address/port
++                                 even when we have no expected connection! */
++                              LOCK_BH(&ip_h323_lock);
++                              info->dir = dir;
++                              info->seq[IP_CT_DIR_REPLY] = ntohl(tcph->seq) + i;
++                              info->offset[IP_CT_DIR_REPLY] = i;
++                              UNLOCK_BH(&ip_h323_lock);
++                      }
++#endif
++              }
++      }
++
++      return NF_ACCEPT;
++
++}
++
++static struct ip_conntrack_helper h225 = 
++      { { NULL, NULL },
++        "H.225",                                      /* name */
++        IP_CT_HELPER_F_REUSE_EXPECT,                  /* flags */
++        THIS_MODULE,                                  /* module */
++        2,                                            /* max_expected */
++        240,                                          /* timeout */
++        { { 0, { __constant_htons(H225_PORT) } },     /* tuple */
++          { 0, { 0 }, IPPROTO_TCP } },
++        { { 0, { 0xFFFF } },                          /* mask */
++          { 0, { 0 }, 0xFFFF } },
++        h225_help                                     /* helper */
++      };
++
++static int __init init(void)
++{
++      return ip_conntrack_helper_register(&h225);
++}
++
++static void __exit fini(void)
++{
++      /* Unregister H.225 helper */   
++      ip_conntrack_helper_unregister(&h225);
++}
++
++EXPORT_SYMBOL(ip_h323_lock);
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_irc.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_irc.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_irc.c     Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_irc.c Wed Sep 24 09:16:26 2003
+@@ -59,7 +59,7 @@
+       {"TSEND ", 6},
+       {"SCHAT ", 6}
+ };
+-#define MAXMATCHLEN   6
++#define MINMATCHLEN   5
+ DECLARE_LOCK(ip_irc_lock);
+ struct module *ip_conntrack_irc = THIS_MODULE;
+@@ -92,9 +92,11 @@
+       *ip = simple_strtoul(data, &data, 10);
+       /* skip blanks between ip and port */
+-      while (*data == ' ')
++      while (*data == ' ') {
++              if (data >= data_end) 
++                      return -1;
+               data++;
+-
++      }
+       *port = simple_strtoul(data, &data, 10);
+       *ad_end_p = data;
+@@ -153,13 +155,17 @@
+       }
+       data_limit = (char *) data + datalen;
+-      while (data < (data_limit - (22 + MAXMATCHLEN))) {
++
++      /* strlen("\1DCC SEND t AAAAAAAA P\1\n")=24
++       *         5+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14 */
++      while (data < (data_limit - (19 + MINMATCHLEN))) {
+               if (memcmp(data, "\1DCC ", 5)) {
+                       data++;
+                       continue;
+               }
+               data += 5;
++              /* we have at least (19+MINMATCHLEN)-5 bytes valid data left */
+               DEBUGP("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u...\n",
+                       NIPQUAD(iph->saddr), ntohs(tcph->source),
+@@ -174,6 +180,9 @@
+                       DEBUGP("DCC %s detected\n", dccprotos[i].match);
+                       data += dccprotos[i].matchlen;
++                      /* we have at least
++                       * (19+MINMATCHLEN)-5-dccprotos[i].matchlen bytes valid
++                       * data left (== 14/13 bytes) */
+                       if (parse_dcc((char *) data, data_limit, &dcc_ip,
+                                      &dcc_port, &addr_beg_p, &addr_end_p)) {
+                               /* unable to parse */
+@@ -183,7 +192,10 @@
+                       DEBUGP("DCC bound ip/port: %u.%u.%u.%u:%u\n",
+                               HIPQUAD(dcc_ip), dcc_port);
+-                      if (ct->tuplehash[dir].tuple.src.ip != htonl(dcc_ip)) {
++                      /* dcc_ip can be the internal OR external (NAT'ed) IP
++                       * Tiago Sousa <mirage@kaotik.org> */
++                      if (ct->tuplehash[dir].tuple.src.ip != htonl(dcc_ip)
++                          && ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip != htonl(dcc_ip)) {
+                               if (net_ratelimit())
+                                       printk(KERN_WARNING
+                                               "Forged DCC command from "
+@@ -209,11 +221,11 @@
+                       exp->tuple = ((struct ip_conntrack_tuple)
+                               { { 0, { 0 } },
+-                                { htonl(dcc_ip), { htons(dcc_port) },
++                                { ct->tuplehash[dir].tuple.src.ip, { .tcp = { htons(dcc_port) } },
+                                   IPPROTO_TCP }});
+                       exp->mask = ((struct ip_conntrack_tuple)
+                               { { 0, { 0 } },
+-                                { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
++                                { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
+                       exp->expectfn = NULL;
+@@ -259,8 +271,6 @@
+       for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+               hlpr = &irc_helpers[i];
+-              memset(hlpr, 0,
+-                     sizeof(struct ip_conntrack_helper));
+               hlpr->tuple.src.u.tcp.port = htons(ports[i]);
+               hlpr->tuple.dst.protonum = IPPROTO_TCP;
+               hlpr->mask.src.u.tcp.port = 0xFFFF;
+@@ -305,9 +315,7 @@
+       }
+ }
+-#ifdef CONFIG_IP_NF_NAT_NEEDED
+ EXPORT_SYMBOL(ip_irc_lock);
+-#endif
+ module_init(init);
+ module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_mms.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_mms.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_mms.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_mms.c Wed Sep 24 09:17:48 2003
+@@ -0,0 +1,308 @@
++/* MMS extension for IP connection tracking
++ * (C) 2002 by Filip Sneppe <filip.sneppe@cronos.be>
++ * based on ip_conntrack_ftp.c and ip_conntrack_irc.c
++ *
++ * ip_conntrack_mms.c v0.3 2002-09-22
++ *
++ *      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_mms.o ports=port1,port2,...port<MAX_PORTS>
++ *
++ *      Please give the ports of all MMS servers You wish to connect to.
++ *      If you don't specify ports, the default will be TCP port 1755.
++ *
++ *      More info on MMS protocol, firewalls and NAT:
++ *      http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/MMSFirewall.asp
++ *      http://www.microsoft.com/windows/windowsmedia/serve/firewall.asp
++ *
++ *      The SDP project people are reverse-engineering MMS:
++ *      http://get.to/sdp
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/ip.h>
++#include <linux/ctype.h>
++#include <net/checksum.h>
++#include <net/tcp.h>
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_mms.h>
++
++DECLARE_LOCK(ip_mms_lock);
++struct module *ip_conntrack_mms = THIS_MODULE;
++
++#define MAX_PORTS 8
++static int ports[MAX_PORTS];
++static int ports_c;
++#ifdef MODULE_PARM
++MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
++#endif
++
++#if 0 
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++EXPORT_SYMBOL(ip_mms_lock);
++
++MODULE_AUTHOR("Filip Sneppe <filip.sneppe@cronos.be>");
++MODULE_DESCRIPTION("Microsoft Windows Media Services (MMS) connection tracking module");
++MODULE_LICENSE("GPL");
++
++/* #define isdigit(c) (c >= '0' && c <= '9') */
++
++/* copied from drivers/usb/serial/io_edgeport.c - not perfect but will do the trick */
++static void unicode_to_ascii (char *string, short *unicode, int unicode_size)
++{
++      int i;
++      for (i = 0; i < unicode_size; ++i) {
++              string[i] = (char)(unicode[i]);
++      }
++      string[unicode_size] = 0x00;
++}
++
++__inline static int atoi(char *s) 
++{
++      int i=0;
++      while (isdigit(*s)) {
++              i = i*10 + *(s++) - '0';
++      }
++      return i;
++}
++
++/* convert ip address string like "192.168.0.10" to unsigned int */
++__inline static u_int32_t asciiiptoi(char *s)
++{
++      unsigned int i, j, k;
++
++      for(i=k=0; k<3; ++k, ++s, i<<=8) {
++              i+=atoi(s);
++              for(j=0; (*(++s) != '.') && (j<3); ++j)
++                      ;
++      }
++      i+=atoi(s);
++      return ntohl(i);
++}
++
++int parse_mms(const char *data, 
++            const unsigned int datalen,
++            u_int32_t *mms_ip,
++            u_int16_t *mms_proto,
++            u_int16_t *mms_port,
++            char **mms_string_b,
++            char **mms_string_e,
++            char **mms_padding_e)
++{
++      int unicode_size, i;
++      char tempstring[28];       /* "\\255.255.255.255\UDP\65535" */
++      char getlengthstring[28];
++      
++      for(unicode_size=0; 
++          (char) *(data+(MMS_SRV_UNICODE_STRING_OFFSET+unicode_size*2)) != (char)0;
++          unicode_size++)
++              if ((unicode_size == 28) || (MMS_SRV_UNICODE_STRING_OFFSET+unicode_size*2 >= datalen)) 
++                      return -1; /* out of bounds - incomplete packet */
++      
++      unicode_to_ascii(tempstring, (short *)(data+MMS_SRV_UNICODE_STRING_OFFSET), unicode_size);
++      DEBUGP("ip_conntrack_mms: offset 60: %s\n", (const char *)(tempstring));
++      
++      /* IP address ? */
++      *mms_ip = asciiiptoi(tempstring+2);
++      
++      i=sprintf(getlengthstring, "%u.%u.%u.%u", HIPQUAD(*mms_ip));
++              
++      /* protocol ? */
++      if(strncmp(tempstring+3+i, "TCP", 3)==0)
++              *mms_proto = IPPROTO_TCP;
++      else if(strncmp(tempstring+3+i, "UDP", 3)==0)
++              *mms_proto = IPPROTO_UDP;
++
++      /* port ? */
++      *mms_port = atoi(tempstring+7+i);
++
++      /* we store a pointer to the beginning of the "\\a.b.c.d\proto\port" 
++         unicode string, one to the end of the string, and one to the end 
++         of the packet, since we must keep track of the number of bytes 
++         between end of the unicode string and the end of packet (padding) */
++      *mms_string_b  = (char *)(data + MMS_SRV_UNICODE_STRING_OFFSET);
++      *mms_string_e  = (char *)(data + MMS_SRV_UNICODE_STRING_OFFSET + unicode_size * 2);
++      *mms_padding_e = (char *)(data + datalen); /* looks funny, doesn't it */
++      return 0;
++}
++
++
++/* FIXME: This should be in userspace.  Later. */
++static int help(const struct iphdr *iph, size_t len,
++              struct ip_conntrack *ct,
++              enum ip_conntrack_info ctinfo)
++{
++      /* tcplen not negative guaranteed by ip_conntrack_tcp.c */
++      struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
++      const char *data = (const char *)tcph + tcph->doff * 4;
++      unsigned int tcplen = len - iph->ihl * 4;
++      unsigned int datalen = tcplen - tcph->doff * 4;
++      int dir = CTINFO2DIR(ctinfo);
++      struct ip_conntrack_expect expect, *exp = &expect; 
++      struct ip_ct_mms_expect *exp_mms_info = &exp->help.exp_mms_info;
++      
++      u_int32_t mms_ip;
++      u_int16_t mms_proto;
++      char mms_proto_string[8];
++      u_int16_t mms_port;
++      char *mms_string_b, *mms_string_e, *mms_padding_e;
++           
++      /* 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_conntrack_mms: Conntrackinfo = %u\n", ctinfo);
++              return NF_ACCEPT;
++      }
++
++      /* Not whole TCP header? */
++      if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) {
++              DEBUGP("ip_conntrack_mms: 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("mms_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;
++      }
++      
++      /* Only look at packets with 0x00030002/196610 on bytes 36->39 of TCP payload */
++      /* FIXME: There is an issue with only looking at this packet: before this packet, 
++         the client has already sent a packet to the server with the server's hostname 
++         according to the client (think of it as the "Host: " header in HTTP/1.1). The 
++         server will break the connection if this doesn't correspond to its own host 
++         header. The client can also connect to an IP address; if it's the server's IP
++         address, it will not break the connection. When doing DNAT on a connection 
++         where the client uses a server's IP address, the nat module should detect
++         this and change this string accordingly to the DNATed address. This should
++         probably be done by checking for an IP address, then storing it as a member
++         of struct ip_ct_mms_expect and checking for it in ip_nat_mms...
++         */
++      if( (MMS_SRV_MSG_OFFSET < datalen) && 
++          ((*(u32 *)(data+MMS_SRV_MSG_OFFSET)) == MMS_SRV_MSG_ID)) {
++              DEBUGP("ip_conntrack_mms: offset 37: %u %u %u %u, datalen:%u\n", 
++                     (u8)*(data+36), (u8)*(data+37), 
++                     (u8)*(data+38), (u8)*(data+39),
++                     datalen);
++              if(parse_mms(data, datalen, &mms_ip, &mms_proto, &mms_port,
++                           &mms_string_b, &mms_string_e, &mms_padding_e))
++                      if(net_ratelimit())
++                              /* FIXME: more verbose debugging ? */
++                              printk(KERN_WARNING
++                                     "ip_conntrack_mms: Unable to parse data payload\n");
++
++              memset(&expect, 0, sizeof(expect));
++
++              sprintf(mms_proto_string, "(%u)", mms_proto);
++              DEBUGP("ip_conntrack_mms: adding %s expectation %u.%u.%u.%u -> %u.%u.%u.%u:%u\n",
++                     mms_proto == IPPROTO_TCP ? "TCP"
++                     : mms_proto == IPPROTO_UDP ? "UDP":mms_proto_string,
++                     NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
++                     NIPQUAD(mms_ip),
++                     mms_port);
++              
++              /* it's possible that the client will just ask the server to tunnel
++                 the stream over the same TCP session (from port 1755): there's 
++                 shouldn't be a need to add an expectation in that case, but it
++                 makes NAT packet mangling so much easier */
++              LOCK_BH(&ip_mms_lock);
++
++              DEBUGP("ip_conntrack_mms: tcph->seq = %u\n", tcph->seq);
++              
++              exp->seq = ntohl(tcph->seq) + (mms_string_b - data);
++              exp_mms_info->len     = (mms_string_e  - mms_string_b);
++              exp_mms_info->padding = (mms_padding_e - mms_string_e);
++              exp_mms_info->port    = mms_port;
++              
++              DEBUGP("ip_conntrack_mms: wrote info seq=%u (ofs=%u), len=%d, padding=%u\n",
++                     exp->seq, (mms_string_e - data), exp_mms_info->len, exp_mms_info->padding);
++              
++              exp->tuple = ((struct ip_conntrack_tuple)
++                            { { ct->tuplehash[!dir].tuple.src.ip, { 0 } },
++                            { mms_ip,
++                              { .tcp = { (__u16) ntohs(mms_port) } },
++                              mms_proto } }
++                           );
++              exp->mask  = ((struct ip_conntrack_tuple)
++                           { { 0xFFFFFFFF, { 0 } },
++                             { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
++              exp->expectfn = NULL;
++              ip_conntrack_expect_related(ct, &expect);
++              UNLOCK_BH(&ip_mms_lock);
++      }
++
++      return NF_ACCEPT;
++}
++
++static struct ip_conntrack_helper mms[MAX_PORTS];
++static char mms_names[MAX_PORTS][10];
++
++/* Not __exit: called from init() */
++static void fini(void)
++{
++      int i;
++      for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
++              DEBUGP("ip_conntrack_mms: unregistering helper for port %d\n",
++                              ports[i]);
++              ip_conntrack_helper_unregister(&mms[i]);
++      }
++}
++
++static int __init init(void)
++{
++      int i, ret;
++      char *tmpname;
++
++      if (ports[0] == 0)
++              ports[0] = MMS_PORT;
++
++      for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
++              memset(&mms[i], 0, sizeof(struct ip_conntrack_helper));
++              mms[i].tuple.src.u.tcp.port = htons(ports[i]);
++              mms[i].tuple.dst.protonum = IPPROTO_TCP;
++              mms[i].mask.src.u.tcp.port = 0xFFFF;
++              mms[i].mask.dst.protonum = 0xFFFF;
++              mms[i].max_expected = 1;
++              mms[i].timeout = 0;
++              mms[i].flags = IP_CT_HELPER_F_REUSE_EXPECT;
++              mms[i].me = THIS_MODULE;
++              mms[i].help = help;
++
++              tmpname = &mms_names[i][0];
++              if (ports[i] == MMS_PORT)
++                      sprintf(tmpname, "mms");
++              else
++                      sprintf(tmpname, "mms-%d", ports[i]);
++              mms[i].name = tmpname;
++
++              DEBUGP("ip_conntrack_mms: registering helper for port %d\n", 
++                              ports[i]);
++              ret = ip_conntrack_helper_register(&mms[i]);
++
++              if (ret) {
++                      fini();
++                      return ret;
++              }
++              ports_c++;
++      }
++      return 0;
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_pptp.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_pptp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_pptp.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_pptp.c        Wed Sep 24 09:18:16 2003
+@@ -0,0 +1,639 @@
++/*
++ * ip_conntrack_pptp.c        - Version 1.9
++ *
++ * Connection tracking support for PPTP (Point to Point Tunneling Protocol).
++ * PPTP is a a protocol for creating virtual private networks.
++ * It is a specification defined by Microsoft and some vendors
++ * working with Microsoft.  PPTP is built on top of a modified
++ * version of the Internet Generic Routing Encapsulation Protocol.
++ * GRE is defined in RFC 1701 and RFC 1702.  Documentation of
++ * PPTP can be found in RFC 2637
++ *
++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org>
++ *
++ * Development of this code funded by Astaro AG (http://www.astaro.com/)
++ *
++ * Limitations:
++ *     - We blindly assume that control connections are always
++ *       established in PNS->PAC direction.  This is a violation
++ *       of RFFC2673
++ *
++ * TODO: - finish support for multiple calls within one session
++ *       (needs expect reservations in newnat)
++ *     - testing of incoming PPTP calls 
++ *
++ * Changes: 
++ *    2002-02-05 - Version 1.3
++ *      - Call ip_conntrack_unexpect_related() from 
++ *        pptp_timeout_related() to destroy expectations in case
++ *        CALL_DISCONNECT_NOTIFY or tcp fin packet was seen
++ *        (Philip Craig <philipc@snapgear.com>)
++ *      - Add Version information at module loadtime
++ *    2002-02-10 - Version 1.6
++ *      - move to C99 style initializers
++ *      - remove second expectation if first arrives
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++#include <net/tcp.h>
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
++
++#define IP_CT_PPTP_VERSION "1.9"
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
++MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP");
++
++DECLARE_LOCK(ip_pptp_lock);
++
++#if 0
++#include "ip_conntrack_pptp_priv.h"
++#define DEBUGP(format, args...)       printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
++                                      ": " format, ## args)
++#else
++#define DEBUGP(format, args...)
++#endif
++
++#define SECS *HZ
++#define MINS * 60 SECS
++#define HOURS * 60 MINS
++#define DAYS * 24 HOURS
++
++#define PPTP_GRE_TIMEOUT              (10 MINS)
++#define PPTP_GRE_STREAM_TIMEOUT       (5 DAYS)
++
++static int pptp_expectfn(struct ip_conntrack *ct)
++{
++      struct ip_conntrack *master;
++      struct ip_conntrack_expect *exp;
++
++      DEBUGP("increasing timeouts\n");
++      /* increase timeout of GRE data channel conntrack entry */
++      ct->proto.gre.timeout = PPTP_GRE_TIMEOUT;
++      ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT;
++
++      master = master_ct(ct);
++      if (!master) {
++              DEBUGP(" no master!!!\n");
++              return 0;
++      }
++
++      exp = ct->master;
++      if (!exp) {
++              DEBUGP("no expectation!!\n");
++              return 0;
++      }
++
++      DEBUGP("completing tuples with ct info\n");
++      /* we can do this, since we're unconfirmed */
++      if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == 
++              htonl(master->help.ct_pptp_info.pac_call_id)) { 
++              /* assume PNS->PAC */
++              ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = 
++                      htonl(master->help.ct_pptp_info.pns_call_id);
++              ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
++                      htonl(master->help.ct_pptp_info.pns_call_id);
++      } else {
++              /* assume PAC->PNS */
++              ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
++                      htonl(master->help.ct_pptp_info.pac_call_id);
++              ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
++                      htonl(master->help.ct_pptp_info.pac_call_id);
++      }
++      
++      /* delete other expectation */
++      if (exp->expected_list.next != &exp->expected_list) {
++              struct ip_conntrack_expect *other_exp;
++              struct list_head *cur_item, *next;
++
++              for (cur_item = master->sibling_list.next;
++                   cur_item != &master->sibling_list; cur_item = next) {
++                      next = cur_item->next;
++                      other_exp = list_entry(cur_item,
++                                             struct ip_conntrack_expect,
++                                             expected_list);
++                      /* remove only if occurred at same sequence number */
++                      if (other_exp != exp && other_exp->seq == exp->seq) {
++                              DEBUGP("unexpecting other direction\n");
++                              ip_ct_gre_keymap_destroy(other_exp);
++                              ip_conntrack_unexpect_related(other_exp);
++                      }
++              }
++      }
++
++      return 0;
++}
++
++/* timeout GRE data connections */
++static int pptp_timeout_related(struct ip_conntrack *ct)
++{
++      struct list_head *cur_item, *next;
++      struct ip_conntrack_expect *exp;
++
++      /* FIXME: do we have to lock something ? */
++      for (cur_item = ct->sibling_list.next;
++          cur_item != &ct->sibling_list; cur_item = next) {
++              next = cur_item->next;
++              exp = list_entry(cur_item, struct ip_conntrack_expect,
++                               expected_list);
++
++              ip_ct_gre_keymap_destroy(exp);
++              if (!exp->sibling) {
++                      ip_conntrack_unexpect_related(exp);
++                      continue;
++              }
++
++              DEBUGP("killing conntrack %p\n",
++                      exp->sibling);
++              exp->sibling->proto.gre.timeout = 0;
++              exp->sibling->proto.gre.stream_timeout = 0;
++
++              if (del_timer(&exp->sibling->timeout))
++                      ip_ct_death_by_timeout((unsigned long)exp->sibling);
++      }
++
++      return 0;
++}
++
++/* expect GRE connections (PNS->PAC and PAC->PNS direction) */
++static inline int
++exp_gre(struct ip_conntrack *master,
++      u_int32_t seq,
++      u_int16_t callid,
++      u_int16_t peer_callid)
++{
++      struct ip_conntrack_expect exp;
++      struct ip_conntrack_tuple inv_tuple;
++
++      memset(&exp, 0, sizeof(exp));
++      /* tuple in original direction, PNS->PAC */
++      exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++      exp.tuple.src.u.gre.key = htonl(ntohs(peer_callid));
++      exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
++      exp.tuple.dst.u.gre.key = htonl(ntohs(callid));
++      exp.tuple.dst.u.gre.protocol = __constant_htons(GRE_PROTOCOL_PPTP);
++      exp.tuple.dst.u.gre.version = GRE_VERSION_PPTP;
++      exp.tuple.dst.protonum = IPPROTO_GRE;
++
++      exp.mask.src.ip = 0xffffffff;
++      exp.mask.src.u.all = 0;
++      exp.mask.dst.u.all = 0;
++      exp.mask.dst.u.gre.key = 0xffffffff;
++      exp.mask.dst.u.gre.version = 0xff;
++      exp.mask.dst.u.gre.protocol = 0xffff;
++      exp.mask.dst.ip = 0xffffffff;
++      exp.mask.dst.protonum = 0xffff;
++                      
++      exp.seq = seq;
++      exp.expectfn = pptp_expectfn;
++
++      exp.help.exp_pptp_info.pac_call_id = ntohs(callid);
++      exp.help.exp_pptp_info.pns_call_id = ntohs(peer_callid);
++
++      DEBUGP("calling expect_related ");
++      DUMP_TUPLE_RAW(&exp.tuple);
++      
++      /* Add GRE keymap entries */
++      if (ip_ct_gre_keymap_add(&exp, &exp.tuple, 0) != 0)
++              return 1;
++
++      invert_tuplepr(&inv_tuple, &exp.tuple);
++      if (ip_ct_gre_keymap_add(&exp, &inv_tuple, 1) != 0) {
++              ip_ct_gre_keymap_destroy(&exp);
++              return 1;
++      }
++      
++      if (ip_conntrack_expect_related(master, &exp) != 0) {
++              ip_ct_gre_keymap_destroy(&exp);
++              DEBUGP("cannot expect_related()\n");
++              return 1;
++      }
++
++      /* tuple in reply direction, PAC->PNS */
++      exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
++      exp.tuple.src.u.gre.key = htonl(ntohs(callid));
++      exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++      exp.tuple.dst.u.gre.key = htonl(ntohs(peer_callid));
++
++      DEBUGP("calling expect_related ");
++      DUMP_TUPLE_RAW(&exp.tuple);
++      
++      /* Add GRE keymap entries */
++      ip_ct_gre_keymap_add(&exp, &exp.tuple, 0);
++      invert_tuplepr(&inv_tuple, &exp.tuple);
++      ip_ct_gre_keymap_add(&exp, &inv_tuple, 1);
++      /* FIXME: cannot handle error correctly, since we need to free
++       * the above keymap :( */
++      
++      if (ip_conntrack_expect_related(master, &exp) != 0) {
++              /* free the second pair of keypmaps */
++              ip_ct_gre_keymap_destroy(&exp);
++              DEBUGP("cannot expect_related():\n");
++              return 1;
++      }
++
++      return 0;
++}
++
++static inline int 
++pptp_inbound_pkt(struct tcphdr *tcph,
++               struct pptp_pkt_hdr *pptph, 
++               size_t datalen,
++               struct ip_conntrack *ct,
++               enum ip_conntrack_info ctinfo)
++{
++      struct PptpControlHeader *ctlh;
++        union pptp_ctrl_union pptpReq;
++      
++      struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
++      u_int16_t msg, *cid, *pcid;
++      u_int32_t seq;  
++
++      ctlh = (struct PptpControlHeader *) 
++              ((char *) pptph + sizeof(struct pptp_pkt_hdr));
++      pptpReq.rawreq = (void *) 
++              ((char *) ctlh + sizeof(struct PptpControlHeader));
++
++      msg = ntohs(ctlh->messageType);
++      DEBUGP("inbound control message %s\n", strMName[msg]);
++
++      switch (msg) {
++      case PPTP_START_SESSION_REPLY:
++              /* server confirms new control session */
++              if (info->sstate < PPTP_SESSION_REQUESTED) {
++                      DEBUGP("%s without START_SESS_REQUEST\n",
++                              strMName[msg]);
++                      break;
++              }
++              if (pptpReq.srep->resultCode == PPTP_START_OK)
++                      info->sstate = PPTP_SESSION_CONFIRMED;
++              else 
++                      info->sstate = PPTP_SESSION_ERROR;
++              break;
++
++      case PPTP_STOP_SESSION_REPLY:
++              /* server confirms end of control session */
++              if (info->sstate > PPTP_SESSION_STOPREQ) {
++                      DEBUGP("%s without STOP_SESS_REQUEST\n",
++                              strMName[msg]);
++                      break;
++              }
++              if (pptpReq.strep->resultCode == PPTP_STOP_OK)
++                      info->sstate = PPTP_SESSION_NONE;
++              else
++                      info->sstate = PPTP_SESSION_ERROR;
++              break;
++
++      case PPTP_OUT_CALL_REPLY:
++              /* server accepted call, we now expect GRE frames */
++              if (info->sstate != PPTP_SESSION_CONFIRMED) {
++                      DEBUGP("%s but no session\n", strMName[msg]);
++                      break;
++              }
++              if (info->cstate != PPTP_CALL_OUT_REQ &&
++                  info->cstate != PPTP_CALL_OUT_CONF) {
++                      DEBUGP("%s without OUTCALL_REQ\n", strMName[msg]);
++                      break;
++              }
++              if (pptpReq.ocack->resultCode != PPTP_OUTCALL_CONNECT) {
++                      info->cstate = PPTP_CALL_NONE;
++                      break;
++              }
++
++              cid = &pptpReq.ocack->callID;
++              pcid = &pptpReq.ocack->peersCallID;
++
++              info->pac_call_id = ntohs(*cid);
++              
++              if (htons(info->pns_call_id) != *pcid) {
++                      DEBUGP("%s for unknown callid %u\n",
++                              strMName[msg], ntohs(*pcid));
++                      break;
++              }
++
++              DEBUGP("%s, CID=%X, PCID=%X\n", strMName[msg], 
++                      ntohs(*cid), ntohs(*pcid));
++              
++              info->cstate = PPTP_CALL_OUT_CONF;
++
++              seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph);
++              if (exp_gre(ct, seq, *cid, *pcid) != 0)
++                      printk("ip_conntrack_pptp: error during exp_gre\n");
++              break;
++
++      case PPTP_IN_CALL_REQUEST:
++              /* server tells us about incoming call request */
++              if (info->sstate != PPTP_SESSION_CONFIRMED) {
++                      DEBUGP("%s but no session\n", strMName[msg]);
++                      break;
++              }
++              pcid = &pptpReq.icack->peersCallID;
++              DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
++              info->cstate = PPTP_CALL_IN_REQ;
++              info->pac_call_id= ntohs(*pcid);
++              break;
++
++      case PPTP_IN_CALL_CONNECT:
++              /* server tells us about incoming call established */
++              if (info->sstate != PPTP_SESSION_CONFIRMED) {
++                      DEBUGP("%s but no session\n", strMName[msg]);
++                      break;
++              }
++              if (info->sstate != PPTP_CALL_IN_REP
++                  && info->sstate != PPTP_CALL_IN_CONF) {
++                      DEBUGP("%s but never sent IN_CALL_REPLY\n",
++                              strMName[msg]);
++                      break;
++              }
++
++              pcid = &pptpReq.iccon->peersCallID;
++              cid = &info->pac_call_id;
++
++              if (info->pns_call_id != ntohs(*pcid)) {
++                      DEBUGP("%s for unknown CallID %u\n", 
++                              strMName[msg], ntohs(*cid));
++                      break;
++              }
++
++              DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
++              info->cstate = PPTP_CALL_IN_CONF;
++
++              /* we expect a GRE connection from PAC to PNS */
++              seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph);
++              if (exp_gre(ct, seq, *cid, *pcid) != 0)
++                      printk("ip_conntrack_pptp: error during exp_gre\n");
++
++              break;
++
++      case PPTP_CALL_DISCONNECT_NOTIFY:
++              /* server confirms disconnect */
++              cid = &pptpReq.disc->callID;
++              DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid));
++              info->cstate = PPTP_CALL_NONE;
++
++              /* untrack this call id, unexpect GRE packets */
++              pptp_timeout_related(ct);
++              break;
++
++      case PPTP_WAN_ERROR_NOTIFY:
++              break;
++
++      case PPTP_ECHO_REQUEST:
++      case PPTP_ECHO_REPLY:
++              /* I don't have to explain these ;) */
++              break;
++      default:
++              DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)
++                      ? strMName[msg]:strMName[0], msg);
++              break;
++      }
++
++      return NF_ACCEPT;
++
++}
++
++static inline int
++pptp_outbound_pkt(struct tcphdr *tcph,
++                struct pptp_pkt_hdr *pptph,
++                size_t datalen,
++                struct ip_conntrack *ct,
++                enum ip_conntrack_info ctinfo)
++{
++      struct PptpControlHeader *ctlh;
++        union pptp_ctrl_union pptpReq;
++      struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
++      u_int16_t msg, *cid, *pcid;
++
++      ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
++      pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
++
++      msg = ntohs(ctlh->messageType);
++      DEBUGP("outbound control message %s\n", strMName[msg]);
++
++      switch (msg) {
++      case PPTP_START_SESSION_REQUEST:
++              /* client requests for new control session */
++              if (info->sstate != PPTP_SESSION_NONE) {
++                      DEBUGP("%s but we already have one",
++                              strMName[msg]);
++              }
++              info->sstate = PPTP_SESSION_REQUESTED;
++              break;
++      case PPTP_STOP_SESSION_REQUEST:
++              /* client requests end of control session */
++              info->sstate = PPTP_SESSION_STOPREQ;
++              break;
++
++      case PPTP_OUT_CALL_REQUEST:
++              /* client initiating connection to server */
++              if (info->sstate != PPTP_SESSION_CONFIRMED) {
++                      DEBUGP("%s but no session\n",
++                              strMName[msg]);
++                      break;
++              }
++              info->cstate = PPTP_CALL_OUT_REQ;
++              /* track PNS call id */
++              cid = &pptpReq.ocreq->callID;
++              DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid));
++              info->pns_call_id = ntohs(*cid);
++              break;
++      case PPTP_IN_CALL_REPLY:
++              /* client answers incoming call */
++              if (info->cstate != PPTP_CALL_IN_REQ
++                  && info->cstate != PPTP_CALL_IN_REP) {
++                      DEBUGP("%s without incall_req\n", 
++                              strMName[msg]);
++                      break;
++              }
++              if (pptpReq.icack->resultCode != PPTP_INCALL_ACCEPT) {
++                      info->cstate = PPTP_CALL_NONE;
++                      break;
++              }
++              pcid = &pptpReq.icack->peersCallID;
++              if (info->pac_call_id != ntohs(*pcid)) {
++                      DEBUGP("%s for unknown call %u\n", 
++                              strMName[msg], ntohs(*pcid));
++                      break;
++              }
++              DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*pcid));
++              /* part two of the three-way handshake */
++              info->cstate = PPTP_CALL_IN_REP;
++              info->pns_call_id = ntohs(pptpReq.icack->callID);
++              break;
++
++      case PPTP_CALL_CLEAR_REQUEST:
++              /* client requests hangup of call */
++              if (info->sstate != PPTP_SESSION_CONFIRMED) {
++                      DEBUGP("CLEAR_CALL but no session\n");
++                      break;
++              }
++              /* FUTURE: iterate over all calls and check if
++               * call ID is valid.  We don't do this without newnat,
++               * because we only know about last call */
++              info->cstate = PPTP_CALL_CLEAR_REQ;
++              break;
++      case PPTP_SET_LINK_INFO:
++              break;
++      case PPTP_ECHO_REQUEST:
++      case PPTP_ECHO_REPLY:
++              /* I don't have to explain these ;) */
++              break;
++      default:
++              DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)? 
++                      strMName[msg]:strMName[0], msg);
++              /* unknown: no need to create GRE masq table entry */
++              break;
++      }
++
++      return NF_ACCEPT;
++}
++
++
++/* track caller id inside control connection, call expect_related */
++static int 
++conntrack_pptp_help(const struct iphdr *iph, size_t len,
++                  struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
++
++{
++      struct pptp_pkt_hdr *pptph;
++      
++      struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
++      u_int32_t tcplen = len - iph->ihl * 4;
++      u_int32_t datalen = tcplen - tcph->doff * 4;
++      void *datalimit;
++      int dir = CTINFO2DIR(ctinfo);
++      struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
++
++      int oldsstate, oldcstate;
++      int ret;
++
++      /* don't do any tracking before tcp handshake complete */
++      if (ctinfo != IP_CT_ESTABLISHED 
++          && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
++              DEBUGP("ctinfo = %u, skipping\n", ctinfo);
++              return NF_ACCEPT;
++      }
++      
++      /* not a complete TCP header? */
++      if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
++              DEBUGP("tcplen = %u\n", tcplen);
++              return NF_ACCEPT;
++      }
++
++      /* checksum invalid? */
++      if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
++                      csum_partial((char *) tcph, tcplen, 0))) {
++              printk(KERN_NOTICE __FILE__ ": bad csum\n");
++              /* W2K PPTP server sends TCP packets with wrong checksum :(( */
++              //return NF_ACCEPT;
++      }
++
++      if (tcph->fin || tcph->rst) {
++              DEBUGP("RST/FIN received, timeouting GRE\n");
++              /* can't do this after real newnat */
++              info->cstate = PPTP_CALL_NONE;
++
++              /* untrack this call id, unexpect GRE packets */
++              pptp_timeout_related(ct);
++      }
++
++
++      pptph = (struct pptp_pkt_hdr *) ((void *) tcph + tcph->doff * 4);
++      datalimit = (void *) pptph + datalen;
++
++      /* not a full pptp packet header? */
++      if ((void *) pptph+sizeof(*pptph) >= datalimit) {
++              DEBUGP("no full PPTP header, can't track\n");
++              return NF_ACCEPT;
++      }
++      
++      /* if it's not a control message we can't do anything with it */
++        if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
++          ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
++              DEBUGP("not a control packet\n");
++              return NF_ACCEPT;
++      }
++
++      oldsstate = info->sstate;
++      oldcstate = info->cstate;
++
++      LOCK_BH(&ip_pptp_lock);
++
++      /* FIXME: We just blindly assume that the control connection is always
++       * established from PNS->PAC.  However, RFC makes no guarantee */
++      if (dir == IP_CT_DIR_ORIGINAL)
++              /* client -> server (PNS -> PAC) */
++              ret = pptp_outbound_pkt(tcph, pptph, datalen, ct, ctinfo);
++      else
++              /* server -> client (PAC -> PNS) */
++              ret = pptp_inbound_pkt(tcph, pptph, datalen, ct, ctinfo);
++      DEBUGP("sstate: %d->%d, cstate: %d->%d\n",
++              oldsstate, info->sstate, oldcstate, info->cstate);
++      UNLOCK_BH(&ip_pptp_lock);
++
++      return ret;
++}
++
++/* control protocol helper */
++static struct ip_conntrack_helper pptp = { 
++      .list = { NULL, NULL },
++      .name = "pptp", 
++      .flags = IP_CT_HELPER_F_REUSE_EXPECT,
++      .me = THIS_MODULE,
++      .max_expected = 2,
++      .timeout = 0,
++      .tuple = { .src = { .ip = 0, 
++                          .u = { .tcp = { .port =  
++                                  __constant_htons(PPTP_CONTROL_PORT) } } 
++                        }, 
++                 .dst = { .ip = 0, 
++                          .u = { .all = 0 },
++                          .protonum = IPPROTO_TCP
++                        } 
++               },
++      .mask = { .src = { .ip = 0, 
++                         .u = { .tcp = { .port = 0xffff } } 
++                       }, 
++                .dst = { .ip = 0, 
++                         .u = { .all = 0 },
++                         .protonum = 0xffff 
++                       } 
++              },
++      .help = conntrack_pptp_help
++};
++
++/* ip_conntrack_pptp initialization */
++static int __init init(void)
++{
++      int retcode;
++
++      DEBUGP(__FILE__ ": registering helper\n");
++      if ((retcode = ip_conntrack_helper_register(&pptp))) {
++                printk(KERN_ERR "Unable to register conntrack application "
++                              "helper for pptp: %d\n", retcode);
++              return -EIO;
++      }
++
++      printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION);
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ip_conntrack_helper_unregister(&pptp);
++      printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION);
++}
++
++module_init(init);
++module_exit(fini);
++
++EXPORT_SYMBOL(ip_pptp_lock);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_pptp_priv.h linux-2.4.20/net/ipv4/netfilter/ip_conntrack_pptp_priv.h
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_pptp_priv.h       Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_pptp_priv.h   Wed Sep 24 09:17:55 2003
+@@ -0,0 +1,24 @@
++#ifndef _IP_CT_PPTP_PRIV_H
++#define _IP_CT_PPTP_PRIV_H
++
++/* PptpControlMessageType names */
++static const char *strMName[] = {
++      "UNKNOWN_MESSAGE",
++      "START_SESSION_REQUEST",
++      "START_SESSION_REPLY",
++      "STOP_SESSION_REQUEST",
++      "STOP_SESSION_REPLY",
++      "ECHO_REQUEST",
++      "ECHO_REPLY",
++      "OUT_CALL_REQUEST",
++      "OUT_CALL_REPLY",
++      "IN_CALL_REQUEST",
++      "IN_CALL_REPLY",
++      "IN_CALL_CONNECT",
++      "CALL_CLEAR_REQUEST",
++      "CALL_DISCONNECT_NOTIFY",
++      "WAN_ERROR_NOTIFY",
++      "SET_LINK_INFO"
++};
++
++#endif
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_proto_generic.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_proto_generic.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_proto_generic.c   Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_proto_generic.c       Wed Sep 24 09:16:22 2003
+@@ -4,7 +4,7 @@
+ #include <linux/netfilter.h>
+ #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+-#define GENERIC_TIMEOUT (600*HZ)
++unsigned long ip_ct_generic_timeout = 600*HZ;
+ static int generic_pkt_to_tuple(const void *datah, size_t datalen,
+                               struct ip_conntrack_tuple *tuple)
+@@ -43,7 +43,7 @@
+                      struct iphdr *iph, size_t len,
+                      enum ip_conntrack_info conntrackinfo)
+ {
+-      ip_ct_refresh(conntrack, GENERIC_TIMEOUT);
++      ip_ct_refresh(conntrack, ip_ct_generic_timeout);
+       return NF_ACCEPT;
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_proto_gre.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_proto_gre.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_proto_gre.c       Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_proto_gre.c   Wed Sep 24 09:17:55 2003
+@@ -0,0 +1,343 @@
++/*
++ * ip_conntrack_proto_gre.c - Version 1.2 
++ *
++ * Connection tracking protocol helper module for GRE.
++ *
++ * GRE is a generic encapsulation protocol, which is generally not very
++ * suited for NAT, as it has no protocol-specific part as port numbers.
++ *
++ * It has an optional key field, which may help us distinguishing two 
++ * connections between the same two hosts.
++ *
++ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 
++ *
++ * PPTP is built on top of a modified version of GRE, and has a mandatory
++ * field called "CallID", which serves us for the same purpose as the key
++ * field in plain GRE.
++ *
++ * Documentation about PPTP can be found in RFC 2637
++ *
++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org>
++ *
++ * Development of this code funded by Astaro AG (http://www.astaro.com/)
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/timer.h>
++#include <linux/netfilter.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/list.h>
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++
++DECLARE_RWLOCK(ip_ct_gre_lock);
++#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_ct_gre_lock)
++#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_ct_gre_lock)
++
++#include <linux/netfilter_ipv4/listhelp.h>
++#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_core.h>
++
++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
++MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE");
++
++/* shamelessly stolen from ip_conntrack_proto_udp.c */
++#define GRE_TIMEOUT           (30*HZ)
++#define GRE_STREAM_TIMEOUT    (180*HZ)
++
++#if 0
++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
++                                     ": " format, ## args)
++#define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x:%u:0x%x\n", \
++                      NIPQUAD((x)->src.ip), ntohl((x)->src.u.gre.key), \
++                      NIPQUAD((x)->dst.ip), ntohl((x)->dst.u.gre.key), \
++                      (x)->dst.u.gre.version, \
++                      ntohs((x)->dst.u.gre.protocol))
++#else
++#define DEBUGP(x, args...)
++#define DUMP_TUPLE_GRE(x)
++#endif
++                              
++/* GRE KEYMAP HANDLING FUNCTIONS */
++static LIST_HEAD(gre_keymap_list);
++
++static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km,
++                              const struct ip_conntrack_tuple *t)
++{
++      return ((km->tuple.src.ip == t->src.ip) &&
++              (km->tuple.dst.ip == t->dst.ip) &&
++              (km->tuple.dst.protonum == t->dst.protonum) &&
++              (km->tuple.dst.u.all == t->dst.u.all));
++}
++
++/* look up the source key for a given tuple */
++static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t)
++{
++      struct ip_ct_gre_keymap *km;
++      u_int32_t key;
++
++      READ_LOCK(&ip_ct_gre_lock);
++      km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
++                      struct ip_ct_gre_keymap *, t);
++      if (!km) {
++              READ_UNLOCK(&ip_ct_gre_lock);
++              return 0;
++      }
++
++      key = km->tuple.src.u.gre.key;
++      READ_UNLOCK(&ip_ct_gre_lock);
++
++      return key;
++}
++
++/* add a single keymap entry, associate with specified expect */
++int ip_ct_gre_keymap_add(struct ip_conntrack_expect *exp,
++                       struct ip_conntrack_tuple *t, int reply)
++{
++      struct ip_ct_gre_keymap *km;
++
++      km = kmalloc(sizeof(*km), GFP_ATOMIC);
++      if (!km)
++              return -1;
++
++      /* initializing list head should be sufficient */
++      memset(km, 0, sizeof(*km));
++
++      memcpy(&km->tuple, t, sizeof(*t));
++
++      if (!reply)
++              exp->proto.gre.keymap_orig = km;
++      else
++              exp->proto.gre.keymap_reply = km;
++
++      DEBUGP("adding new entry %p: ", km);
++      DUMP_TUPLE_GRE(&km->tuple);
++
++      WRITE_LOCK(&ip_ct_gre_lock);
++      list_append(&gre_keymap_list, km);
++      WRITE_UNLOCK(&ip_ct_gre_lock);
++
++      return 0;
++}
++
++/* change the tuple of a keymap entry (used by nat helper) */
++void ip_ct_gre_keymap_change(struct ip_ct_gre_keymap *km,
++                           struct ip_conntrack_tuple *t)
++{
++      DEBUGP("changing entry %p to: ", km);
++      DUMP_TUPLE_GRE(t);
++
++      WRITE_LOCK(&ip_ct_gre_lock);
++      memcpy(&km->tuple, t, sizeof(km->tuple));
++      WRITE_UNLOCK(&ip_ct_gre_lock);
++}
++
++/* destroy the keymap entries associated with specified expect */
++void ip_ct_gre_keymap_destroy(struct ip_conntrack_expect *exp)
++{
++      DEBUGP("entering for exp %p\n", exp);
++      WRITE_LOCK(&ip_ct_gre_lock);
++      if (exp->proto.gre.keymap_orig) {
++              DEBUGP("removing %p from list\n", exp->proto.gre.keymap_orig);
++              list_del(&exp->proto.gre.keymap_orig->list);
++              kfree(exp->proto.gre.keymap_orig);
++              exp->proto.gre.keymap_orig = NULL;
++      }
++      if (exp->proto.gre.keymap_reply) {
++              DEBUGP("removing %p from list\n", exp->proto.gre.keymap_reply);
++              list_del(&exp->proto.gre.keymap_reply->list);
++              kfree(exp->proto.gre.keymap_reply);
++              exp->proto.gre.keymap_reply = NULL;
++      }
++      WRITE_UNLOCK(&ip_ct_gre_lock);
++}
++
++
++/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
++
++/* invert gre part of tuple */
++static int gre_invert_tuple(struct ip_conntrack_tuple *tuple,
++                          const struct ip_conntrack_tuple *orig)
++{
++      tuple->dst.u.gre.protocol = orig->dst.u.gre.protocol;
++      tuple->dst.u.gre.version = orig->dst.u.gre.version;
++
++      tuple->dst.u.gre.key = orig->src.u.gre.key;
++      tuple->src.u.gre.key = orig->dst.u.gre.key;
++
++      return 1;
++}
++
++/* gre hdr info to tuple */
++static int gre_pkt_to_tuple(const void *datah, size_t datalen,
++                          struct ip_conntrack_tuple *tuple)
++{
++      struct gre_hdr *grehdr = (struct gre_hdr *) datah;
++      struct gre_hdr_pptp *pgrehdr = (struct gre_hdr_pptp *) datah;
++      u_int32_t srckey;
++
++      /* core guarantees 8 protocol bytes, no need for size check */
++
++      tuple->dst.u.gre.version = grehdr->version; 
++      tuple->dst.u.gre.protocol = grehdr->protocol;
++
++      switch (grehdr->version) {
++              case GRE_VERSION_1701:
++                      if (!grehdr->key) {
++                              DEBUGP("Can't track GRE without key\n");
++                              return 0;
++                      }
++                      tuple->dst.u.gre.key = *(gre_key(grehdr));
++                      break;
++
++              case GRE_VERSION_PPTP:
++                      if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) {
++                              DEBUGP("GRE_VERSION_PPTP but unknown proto\n");
++                              return 0;
++                      }
++                      tuple->dst.u.gre.key = htonl(ntohs(pgrehdr->call_id));
++                      break;
++
++              default:
++                      printk(KERN_WARNING "unknown GRE version %hu\n",
++                              tuple->dst.u.gre.version);
++                      return 0;
++      }
++
++      srckey = gre_keymap_lookup(tuple);
++
++#if 0
++      DEBUGP("found src key %x for tuple ", ntohl(srckey));
++      DUMP_TUPLE_GRE(tuple);
++#endif
++      tuple->src.u.gre.key = srckey;
++
++      return 1;
++}
++
++/* print gre part of tuple */
++static unsigned int gre_print_tuple(char *buffer,
++                                  const struct ip_conntrack_tuple *tuple)
++{
++      return sprintf(buffer, "version=%d protocol=0x%04x srckey=0x%x dstkey=0x%x ", 
++                      tuple->dst.u.gre.version,
++                      ntohs(tuple->dst.u.gre.protocol),
++                      ntohl(tuple->src.u.gre.key),
++                      ntohl(tuple->dst.u.gre.key));
++}
++
++/* print private data for conntrack */
++static unsigned int gre_print_conntrack(char *buffer,
++                                      const struct ip_conntrack *ct)
++{
++      return sprintf(buffer, "timeout=%u, stream_timeout=%u ",
++                     (ct->proto.gre.timeout / HZ),
++                     (ct->proto.gre.stream_timeout / HZ));
++}
++
++/* Returns verdict for packet, and may modify conntrack */
++static int gre_packet(struct ip_conntrack *ct,
++                    struct iphdr *iph, size_t len,
++                    enum ip_conntrack_info conntrackinfo)
++{
++      /* If we've seen traffic both ways, this is a GRE connection.
++       * Extend timeout. */
++      if (ct->status & IPS_SEEN_REPLY) {
++              ip_ct_refresh(ct, ct->proto.gre.stream_timeout);
++              /* Also, more likely to be important, and not a probe. */
++              set_bit(IPS_ASSURED_BIT, &ct->status);
++      } else
++              ip_ct_refresh(ct, ct->proto.gre.timeout);
++      
++      return NF_ACCEPT;
++}
++
++/* Called when a new connection for this protocol found. */
++static int gre_new(struct ip_conntrack *ct,
++                 struct iphdr *iph, size_t len)
++{ 
++      DEBUGP(": ");
++      DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++
++      /* initialize to sane value.  Ideally a conntrack helper
++       * (e.g. in case of pptp) is increasing them */
++      ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT;
++      ct->proto.gre.timeout = GRE_TIMEOUT;
++
++      return 1;
++}
++
++/* Called when a conntrack entry has already been removed from the hashes
++ * and is about to be deleted from memory */
++static void gre_destroy(struct ip_conntrack *ct)
++{
++      struct ip_conntrack_expect *master = ct->master;
++
++      DEBUGP(" entering\n");
++
++      if (!master) {
++              DEBUGP("no master exp for ct %p\n", ct);
++              return;
++      }
++
++      ip_ct_gre_keymap_destroy(master);
++}
++
++/* protocol helper struct */
++static struct ip_conntrack_protocol gre = { { NULL, NULL }, IPPROTO_GRE,
++                                          "gre", 
++                                          gre_pkt_to_tuple,
++                                          gre_invert_tuple,
++                                          gre_print_tuple,
++                                          gre_print_conntrack,
++                                          gre_packet,
++                                          gre_new,
++                                          gre_destroy,
++                                          NULL,
++                                          THIS_MODULE };
++
++/* ip_conntrack_proto_gre initialization */
++static int __init init(void)
++{
++      int retcode;
++
++      if ((retcode = ip_conntrack_protocol_register(&gre))) {
++                printk(KERN_ERR "Unable to register conntrack protocol "
++                              "helper for gre: %d\n", retcode);
++              return -EIO;
++      }
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      struct list_head *pos, *n;
++
++      /* delete all keymap entries */
++      WRITE_LOCK(&ip_ct_gre_lock);
++      list_for_each_safe(pos, n, &gre_keymap_list) {
++              DEBUGP("deleting keymap %p at module unload time\n", pos);
++              list_del(pos);
++              kfree(pos);
++      }
++      WRITE_UNLOCK(&ip_ct_gre_lock);
++
++      ip_conntrack_protocol_unregister(&gre); 
++}
++
++EXPORT_SYMBOL(ip_ct_gre_keymap_add);
++EXPORT_SYMBOL(ip_ct_gre_keymap_change);
++EXPORT_SYMBOL(ip_ct_gre_keymap_destroy);
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_proto_icmp.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_proto_icmp.c      Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_proto_icmp.c  Wed Sep 24 09:16:22 2003
+@@ -6,7 +6,7 @@
+ #include <linux/icmp.h>
+ #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+-#define ICMP_TIMEOUT (30*HZ)
++unsigned long ip_ct_icmp_timeout = 30*HZ;
+ #if 0
+ #define DEBUGP printk
+@@ -82,7 +82,7 @@
+                       ct->timeout.function((unsigned long)ct);
+       } else {
+               atomic_inc(&ct->proto.icmp.count);
+-              ip_ct_refresh(ct, ICMP_TIMEOUT);
++              ip_ct_refresh(ct, ip_ct_icmp_timeout);
+       }
+       return NF_ACCEPT;
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_proto_tcp.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_proto_tcp.c       Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_proto_tcp.c   Wed Sep 24 09:16:22 2003
+@@ -1,4 +1,3 @@
+-#define __NO_VERSION__
+ #include <linux/types.h>
+ #include <linux/sched.h>
+ #include <linux/timer.h>
+@@ -50,20 +49,28 @@
+ #define HOURS * 60 MINS
+ #define DAYS * 24 HOURS
+-
+-static unsigned long tcp_timeouts[]
+-= { 30 MINS,  /*      TCP_CONNTRACK_NONE,     */
+-    5 DAYS,   /*      TCP_CONNTRACK_ESTABLISHED,      */
+-    2 MINS,   /*      TCP_CONNTRACK_SYN_SENT, */
+-    60 SECS,  /*      TCP_CONNTRACK_SYN_RECV, */
+-    2 MINS,   /*      TCP_CONNTRACK_FIN_WAIT, */
+-    2 MINS,   /*      TCP_CONNTRACK_TIME_WAIT,        */
+-    10 SECS,  /*      TCP_CONNTRACK_CLOSE,    */
+-    60 SECS,  /*      TCP_CONNTRACK_CLOSE_WAIT,       */
+-    30 SECS,  /*      TCP_CONNTRACK_LAST_ACK, */
+-    2 MINS,   /*      TCP_CONNTRACK_LISTEN,   */
+-};
+-
++unsigned long ip_ct_tcp_timeout_syn_sent =      2 MINS;
++unsigned long ip_ct_tcp_timeout_syn_recv =     60 SECS;
++unsigned long ip_ct_tcp_timeout_established =   5 DAYS;
++unsigned long ip_ct_tcp_timeout_fin_wait =      2 MINS;
++unsigned long ip_ct_tcp_timeout_close_wait =    3 DAYS;
++unsigned long ip_ct_tcp_timeout_last_ack =     30 SECS;
++unsigned long ip_ct_tcp_timeout_time_wait =     2 MINS;
++unsigned long ip_ct_tcp_timeout_close =        10 SECS;
++
++static unsigned long * tcp_timeouts[]
++= { 0,                                 /*      TCP_CONNTRACK_NONE */
++    &ip_ct_tcp_timeout_established,    /*      TCP_CONNTRACK_ESTABLISHED,      */
++    &ip_ct_tcp_timeout_syn_sent,       /*      TCP_CONNTRACK_SYN_SENT, */
++    &ip_ct_tcp_timeout_syn_recv,       /*      TCP_CONNTRACK_SYN_RECV, */
++    &ip_ct_tcp_timeout_fin_wait,       /*      TCP_CONNTRACK_FIN_WAIT, */
++    &ip_ct_tcp_timeout_time_wait,      /*      TCP_CONNTRACK_TIME_WAIT,        */
++    &ip_ct_tcp_timeout_close,          /*      TCP_CONNTRACK_CLOSE,    */
++    &ip_ct_tcp_timeout_close_wait,     /*      TCP_CONNTRACK_CLOSE_WAIT,       */
++    &ip_ct_tcp_timeout_last_ack,       /*      TCP_CONNTRACK_LAST_ACK, */
++    0,                                 /*      TCP_CONNTRACK_LISTEN */
++ };
++ 
+ #define sNO TCP_CONNTRACK_NONE
+ #define sES TCP_CONNTRACK_ESTABLISHED
+ #define sSS TCP_CONNTRACK_SYN_SENT
+@@ -186,13 +193,13 @@
+           && tcph->syn && tcph->ack)
+               conntrack->proto.tcp.handshake_ack
+                       = htonl(ntohl(tcph->seq) + 1);
+-      WRITE_UNLOCK(&tcp_lock);
+       /* If only reply is a RST, we can consider ourselves not to
+          have an established connection: this is a fairly common
+          problem case, so we can delete the conntrack
+          immediately.  --RR */
+-      if (!(conntrack->status & IPS_SEEN_REPLY) && tcph->rst) {
++      if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) && tcph->rst) {
++              WRITE_UNLOCK(&tcp_lock);
+               if (del_timer(&conntrack->timeout))
+                       conntrack->timeout.function((unsigned long)conntrack);
+       } else {
+@@ -203,7 +210,8 @@
+                   && tcph->ack_seq == conntrack->proto.tcp.handshake_ack)
+                       set_bit(IPS_ASSURED_BIT, &conntrack->status);
+-              ip_ct_refresh(conntrack, tcp_timeouts[newconntrack]);
++              WRITE_UNLOCK(&tcp_lock);
++              ip_ct_refresh(conntrack, *tcp_timeouts[newconntrack]);
+       }
+       return NF_ACCEPT;
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_proto_udp.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_proto_udp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_proto_udp.c       Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_proto_udp.c   Wed Sep 24 09:16:22 2003
+@@ -6,8 +6,8 @@
+ #include <linux/udp.h>
+ #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+-#define UDP_TIMEOUT (30*HZ)
+-#define UDP_STREAM_TIMEOUT (180*HZ)
++unsigned long ip_ct_udp_timeout = 30*HZ;
++unsigned long ip_ct_udp_timeout_stream = 180*HZ;
+ static int udp_pkt_to_tuple(const void *datah, size_t datalen,
+                           struct ip_conntrack_tuple *tuple)
+@@ -51,12 +51,12 @@
+ {
+       /* If we've seen traffic both ways, this is some kind of UDP
+          stream.  Extend timeout. */
+-      if (conntrack->status & IPS_SEEN_REPLY) {
+-              ip_ct_refresh(conntrack, UDP_STREAM_TIMEOUT);
++      if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
++              ip_ct_refresh(conntrack, ip_ct_udp_timeout_stream);
+               /* Also, more likely to be important, and not a probe */
+               set_bit(IPS_ASSURED_BIT, &conntrack->status);
+       } else
+-              ip_ct_refresh(conntrack, UDP_TIMEOUT);
++              ip_ct_refresh(conntrack, ip_ct_udp_timeout);
+       return NF_ACCEPT;
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_quake3.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_quake3.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_quake3.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_quake3.c      Wed Sep 24 09:17:58 2003
+@@ -0,0 +1,156 @@
++/* Quake3 extension for IP connection tracking
++ * (C) 2002 by Filip Sneppe <filip.sneppe@cronos.be>
++ * based on ip_conntrack_ftp.c and ip_conntrack_tftp.c
++ *
++ * ip_conntrack_quake3.c v0.04 2002-08-31
++ *
++ *      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_quake3.o ports=port1,port2,...port<MAX_PORTS>
++ *
++ *      please give the ports of all Quake3 master servers You wish to 
++ *      connect to. If you don't specify ports, the default will be UDP 
++ *      port 27950.
++ *
++ *      Thanks to the Ethereal folks for their analysis of the Quake3 protocol.
++ */
++
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/udp.h>
++
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_quake3.h>
++
++struct module *ip_conntrack_quake3 = THIS_MODULE;
++
++MODULE_AUTHOR("Filip Sneppe <filip.sneppe@cronos.be>");
++MODULE_DESCRIPTION("Netfilter connection tracking module for Quake III Arena");
++MODULE_LICENSE("GPL");
++
++#define MAX_PORTS 8
++static int ports[MAX_PORTS];
++static int ports_c = 0;
++#ifdef MODULE_PARM
++MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
++MODULE_PARM_DESC(ports, "port numbers of Quake III master servers");
++#endif
++
++/* Quake3 master server reply will add > 100 expectations per reply packet; when
++   doing lots of printk's, klogd may not be able to read /proc/kmsg fast enough */
++#if 0 
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++struct quake3_search quake3s_conntrack = { "****", "getserversResponse", sizeof("getserversResponse") - 1 };
++
++static int quake3_help(const struct iphdr *iph, size_t len,
++      struct ip_conntrack *ct,
++      enum ip_conntrack_info ctinfo)
++{
++      struct udphdr *udph = (void *)iph + iph->ihl * 4;
++      int dir = CTINFO2DIR(ctinfo);
++      struct ip_conntrack_expect exp;
++      int i;
++      
++        /* Until there's been traffic both ways, don't look in packets. note: it's UDP ! */
++      if (ctinfo != IP_CT_ESTABLISHED
++          && ctinfo != IP_CT_IS_REPLY) {
++              DEBUGP("ip_conntrack_quake3: not ok ! Conntrackinfo = %u\n", ctinfo);
++              return NF_ACCEPT;
++      } else { DEBUGP("ip_conntrack_quake3: it's ok ! Conntrackinfo = %u\n", ctinfo); }
++      
++      if (strnicmp((const char *)udph + 12, quake3s_conntrack.pattern, quake3s_conntrack.plen) == 0) {
++              for(i=31;    /* 8 bytes UDP hdr, 4 bytes filler, 18 bytes "getserversResponse", 1 byte "\" */
++                  i+6 < ntohs(udph->len);
++                  i+=7) {
++                      DEBUGP("ip_conntrack_quake3: adding server at offset %u/%u %u.%u.%u.%u:%u\n",
++                             i, ntohs(udph->len),
++                             NIPQUAD( (u_int32_t) *( (u_int32_t *)( (int)udph + i ) ) ), 
++                             ntohs((__u16) *( (__u16 *)( (int)udph + i + 4 ) ) ) );
++
++                      memset(&exp, 0, sizeof(exp));
++
++                      exp.tuple = ((struct ip_conntrack_tuple)
++                                   { { ct->tuplehash[!dir].tuple.src.ip, { 0 } },
++                                     { (u_int32_t) *((u_int32_t *)((int)udph + i)), 
++                                     { .udp = { (__u16) *((__u16 *)((int)udph+i+4)) } }, 
++                                       IPPROTO_UDP } }
++                                  );
++                      exp.mask  = ((struct ip_conntrack_tuple)
++                                   { { 0xFFFFFFFF, { 0 } },
++                                     { 0xFFFFFFFF, { .udp = { 0xFFFF } }, 0xFFFF }});
++                      exp.expectfn = NULL;
++
++                      ip_conntrack_expect_related(ct, &exp);
++              }
++
++      }
++      
++      return(NF_ACCEPT);
++}
++
++static struct ip_conntrack_helper quake3[MAX_PORTS];
++static char quake3_names[MAX_PORTS][13];  /* quake3-65535 */
++
++static void fini(void)
++{
++      int i;
++
++      for(i = 0 ; (i < ports_c); i++) {
++              DEBUGP("ip_conntrack_quake3: unregistering helper for port %d\n",
++                                      ports[i]);
++              ip_conntrack_helper_unregister(&quake3[i]);
++      } 
++}
++
++static int __init init(void)
++{
++      int i, ret;
++      char *tmpname;
++
++      if(!ports[0])
++              ports[0]=QUAKE3_MASTER_PORT;
++
++      for(i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) {
++              /* Create helper structure */
++              memset(&quake3[i], 0, sizeof(struct ip_conntrack_helper));
++
++              quake3[i].tuple.dst.protonum = IPPROTO_UDP;
++              quake3[i].tuple.src.u.udp.port = htons(ports[i]);
++              quake3[i].mask.dst.protonum = 0xFFFF;
++              quake3[i].mask.src.u.udp.port = 0xFFFF;
++              quake3[i].help = quake3_help;
++              quake3[i].me = THIS_MODULE;
++
++              tmpname = &quake3_names[i][0];
++              if (ports[i] == QUAKE3_MASTER_PORT)
++                      sprintf(tmpname, "quake3");
++              else
++                      sprintf(tmpname, "quake3-%d", i);
++              quake3[i].name = tmpname;
++              
++              DEBUGP("ip_conntrack_quake3: registering helper for port %d\n",
++                     ports[i]);
++
++              ret=ip_conntrack_helper_register(&quake3[i]);
++              if(ret) {
++                      fini();
++                      return(ret);
++              }
++              ports_c++;
++      }
++
++      return(0);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c     Wed Sep 24 09:18:01 2003
+@@ -0,0 +1,508 @@
++/* RPC extension for IP (TCP) connection tracking, Version 2.2
++ * (C) 2000 by Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>
++ *    - original rpc tracking module
++ *    - "recent" connection handling for kernel 2.3+ netfilter
++ *
++ * (C) 2001 by Rusty Russell <rusty@rustcorp.com.au>
++ *    - upgraded conntrack modules to oldnat api - kernel 2.4.0+
++ *
++ * (C) 2002,2003 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
++ *    - upgraded conntrack modules to newnat api - kernel 2.4.20+
++ *    - extended matching to support filtering on procedures
++ *
++ * ip_conntrack_rpc_tpc.c,v 2.2 2003/01/12 18:30:00
++ *
++ *    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_rpc_tcp.o ports=port1,port2,...port<MAX_PORTS>
++ *
++ *    Please give the ports of all RPC servers you wish to connect to.
++ *    If you don't specify ports, the default will be port 111.
++ **
++ *    Note to all:
++ *
++ *    RPCs should not be exposed to the internet - ask the Pentagon;
++ *
++ *      "The unidentified crackers pleaded guilty in July to charges
++ *       of juvenile delinquency stemming from a string of Pentagon
++ *       network intrusions in February.
++ *
++ *       The youths, going by the names TooShort and Makaveli, used
++ *       a common server security hole to break in, according to
++ *       Dane Jasper, owner of the California Internet service
++ *       provider, Sonic. They used the hole, known as the 'statd'
++ *       exploit, to attempt more than 800 break-ins, Jasper said."
++ *
++ *    From: Wired News; "Pentagon Kids Kicked Off Grid" - Nov 6, 1998
++ *    URL:  http://www.wired.com/news/politics/0,1283,16098,00.html
++ **
++ */
++
++#include <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++#include <net/tcp.h>
++
++#include <asm/param.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/stddef.h>
++#include <linux/list.h>
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_rpc.h>
++
++#define MAX_PORTS 8
++static int ports[MAX_PORTS];
++static int ports_n_c = 0;
++
++#ifdef MODULE_PARM
++MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
++MODULE_PARM_DESC(ports, "port numbers (TCP/UDP) of RPC portmapper servers");
++#endif
++
++MODULE_AUTHOR("Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>");
++MODULE_DESCRIPTION("RPC TCP connection tracking module");
++MODULE_LICENSE("GPL");
++
++#if 0
++#define DEBUGP(format, args...) printk(KERN_DEBUG "ip_conntrack_rpc_tcp: " \
++                                      format, ## args)
++#else
++#define DEBUGP(format, args...)
++#endif
++
++DECLARE_RWLOCK(ipct_rpc_tcp_lock);
++#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock)
++#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++/* For future conections RPC, using client's cache bindings
++ * I'll use ip_conntrack_lock to lock these lists        */
++
++LIST_HEAD(request_p_list_tcp);
++
++
++static void delete_request_p(unsigned long request_p_ul) 
++{
++      struct request_p *p = (void *)request_p_ul;
++      
++      WRITE_LOCK(&ipct_rpc_tcp_lock);
++      LIST_DELETE(&request_p_list_tcp, p);
++      WRITE_UNLOCK(&ipct_rpc_tcp_lock);
++      kfree(p);
++      return;
++}
++
++
++static void req_cl(struct request_p * r)
++{
++      WRITE_LOCK(&ipct_rpc_tcp_lock);
++      del_timer(&r->timeout);
++      LIST_DELETE(&request_p_list_tcp, r);
++      WRITE_UNLOCK(&ipct_rpc_tcp_lock);
++      kfree(r);
++      return;
++}
++
++
++static void clean_request(struct list_head *list)
++{
++      struct list_head *first = list->prev;
++      struct list_head *temp = list->next;
++      struct list_head *aux;
++
++      if (list_empty(list))
++              return;
++
++      while (first != temp) {
++              aux = temp->next;
++              req_cl((struct request_p *)temp);
++              temp = aux;     
++      }
++      req_cl((struct request_p *)temp);
++      return;
++}
++
++
++static void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip,
++                   u_int16_t port)
++{
++      struct request_p *req_p;
++      
++      /* Verifies if entry already exists */
++      WRITE_LOCK(&ipct_rpc_tcp_lock);
++      req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp,
++              struct request_p *, xid, ip, port);
++
++      if (req_p) {
++              /* Refresh timeout */
++              if (del_timer(&req_p->timeout)) {
++                      req_p->timeout.expires = jiffies + EXP;
++                      add_timer(&req_p->timeout);     
++              } 
++              WRITE_UNLOCK(&ipct_rpc_tcp_lock);
++              return; 
++
++      }
++      WRITE_UNLOCK(&ipct_rpc_tcp_lock);
++      
++      /* Allocate new request_p */
++      req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC);
++      if (!req_p) {
++              DEBUGP("can't allocate request_p\n");
++              return;                 
++      }
++      *req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, 
++              { { NULL, NULL }, jiffies + EXP, (unsigned long)req_p,
++                      NULL }}); 
++      
++      /* Initialize timer */
++      init_timer(&req_p->timeout);
++      req_p->timeout.function = delete_request_p;
++      add_timer(&req_p->timeout); 
++
++      /* Put in list */
++      WRITE_LOCK(&ipct_rpc_tcp_lock);
++      list_prepend(&request_p_list_tcp, req_p);
++      WRITE_UNLOCK(&ipct_rpc_tcp_lock); 
++      return; 
++
++}
++
++
++static int check_rpc_packet(const u_int32_t *data,
++                      int dir, struct ip_conntrack *ct,
++                      struct list_head request_p_list)
++{
++      struct request_p *req_p;
++      u_int32_t xid;
++      struct ip_conntrack_expect expect, *exp = &expect;
++
++        /* Translstion's buffer for XDR */
++        u_int16_t port_buf;
++
++
++      /* Get XID */
++      xid = *data;
++
++      /* This does sanity checking on RPC payloads,
++       * and permits only the RPC "get port" (3)
++       * in authorised procedures in client
++       * communications with the portmapper.
++       */
++
++      /* perform direction dependant RPC work */
++      if (dir == IP_CT_DIR_ORIGINAL) {
++
++              data += 5;
++
++              /* Get RPC requestor */
++              if (IXDR_GET_INT32(data) != 3) {
++                      DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n");
++                      return NF_ACCEPT;
++              }
++              DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n");
++
++              data++;
++
++              /* Jump Credentials and Verfifier */
++              data += IXDR_GET_INT32(data) + 2;
++              data += IXDR_GET_INT32(data) + 2;
++
++              /* Get RPC procedure */
++              DEBUGP("RPC packet contains procedure request [%u]. [cont]\n",
++                      (unsigned int)IXDR_GET_INT32(data));
++
++              /* Get RPC protocol and store against client parameters */
++              data = data + 2;
++              alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip,
++                              ct->tuplehash[dir].tuple.src.u.all);
++
++              DEBUGP("allocated RPC req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n",
++                      xid, IXDR_GET_INT32(data),
++                      NIPQUAD(ct->tuplehash[dir].tuple.src.ip),
++                      ntohs(ct->tuplehash[dir].tuple.src.u.all));
++
++              DEBUGP("allocated RPC request for protocol %u. [done]\n",
++                      (unsigned int)IXDR_GET_INT32(data));
++
++      } else {
++
++              /* Check for returning packet's stored counterpart */
++              req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp,
++                                struct request_p *, xid,
++                                ct->tuplehash[!dir].tuple.src.ip,
++                                ct->tuplehash[!dir].tuple.src.u.all);
++
++              /* Drop unexpected packets */
++              if (!req_p) {
++                      DEBUGP("packet is not expected. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++              /* Verifies if packet is really an RPC reply packet */
++              data = data++;
++              if (IXDR_GET_INT32(data) != 1) {
++                      DEBUGP("packet is not a valid RPC reply. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++              /* Is status accept? */
++              data++;
++              if (IXDR_GET_INT32(data)) {
++                      DEBUGP("packet is not an RPC accept. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++              /* Get Verifier length. Jump verifier */
++              data++;
++              data = data + IXDR_GET_INT32(data) + 2;
++
++              /* Is accpet status "success"? */
++              if (IXDR_GET_INT32(data)) {
++                      DEBUGP("packet is not an RPC accept status of success. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++              /* Get server port number */      
++              data++;
++              port_buf = (u_int16_t) IXDR_GET_INT32(data);
++
++              /* If a packet has made it this far then it deserves an
++               * expectation ...  if port == 0, then this service is 
++               * not going to be registered.
++               */
++              if (port_buf) {
++                      DEBUGP("port found: %u\n", port_buf);
++
++                      memset(&expect, 0, sizeof(expect));
++
++                      /* Watch out, Radioactive-Man! */
++                      exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
++                      exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
++                      exp->mask.src.ip = 0xffffffff;
++                      exp->mask.dst.ip = 0xffffffff;
++
++                      switch (req_p->proto) {
++                              case IPPROTO_UDP:
++                                      exp->tuple.src.u.udp.port = 0;
++                                      exp->tuple.dst.u.udp.port = htons(port_buf);
++                                      exp->tuple.dst.protonum = IPPROTO_UDP;
++                                      exp->mask.src.u.udp.port = 0;
++                                      exp->mask.dst.u.udp.port = htons(0xffff);
++                                      exp->mask.dst.protonum = 0xffff;
++                                      break;
++
++                              case IPPROTO_TCP:
++                                      exp->tuple.src.u.tcp.port = 0;
++                                      exp->tuple.dst.u.tcp.port = htons(port_buf);
++                                      exp->tuple.dst.protonum = IPPROTO_TCP;
++                                      exp->mask.src.u.tcp.port = 0;
++                                      exp->mask.dst.u.tcp.port = htons(0xffff);
++                                      exp->mask.dst.protonum = 0xffff;
++                                      break;
++                      }
++                      exp->expectfn = NULL;
++
++                      ip_conntrack_expect_related(ct, &expect);
++
++                      DEBUGP("expect related ip   %u.%u.%u.%u:0-%u.%u.%u.%u:%u proto=%u\n",
++                              NIPQUAD(exp->tuple.src.ip),
++                              NIPQUAD(exp->tuple.dst.ip),
++                              port_buf, req_p->proto);
++
++                      DEBUGP("expect related mask %u.%u.%u.%u:0-%u.%u.%u.%u:65535 proto=%u\n",
++                              NIPQUAD(exp->mask.src.ip),
++                              NIPQUAD(exp->mask.dst.ip),
++                              exp->mask.dst.protonum);
++
++              }
++
++              req_cl(req_p);
++
++              DEBUGP("packet evaluated. [expect]\n");
++              return NF_ACCEPT;
++      }
++
++      return NF_ACCEPT;
++
++}
++
++
++/* RPC TCP helper */
++static int help(const struct iphdr *iph, size_t len,
++              struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
++{
++      struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
++      const u_int32_t *data = (const u_int32_t *)tcph + tcph->doff;
++      size_t tcplen = len - iph->ihl * 4;
++
++      int dir = CTINFO2DIR(ctinfo);
++      int crp_ret;
++
++
++      DEBUGP("new packet to evaluate ..\n");
++
++      /* This works for packets like handshake packets, ignore */
++      if (len == ((tcph->doff + iph->ihl) * 4)) {
++              DEBUGP("packet has no data (may still be handshaking). [skip]\n");
++              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("connection tracking state is; ctinfo=%u ..\n", ctinfo);
++              DEBUGP("[note: failure to get past this error may indicate asymmetric routing]\n");
++              DEBUGP("packet is not yet part of a two way stream. [skip]\n");
++              return NF_ACCEPT;
++      }
++
++      /* Not whole TCP header? */
++      if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
++              DEBUGP("TCP header length is; tcplen=%u ..\n", (unsigned) tcplen);
++              DEBUGP("packet does not contain a complete TCP header. [skip]\n");
++              return NF_ACCEPT;
++      }
++
++      /* FIXME: Source route IP option packets --RR */
++      if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
++                       csum_partial((char *) tcph, tcplen, 0))) {
++              DEBUGP("csum; %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
++                   tcph, tcplen, NIPQUAD(iph->saddr),
++                   NIPQUAD(iph->daddr));
++              DEBUGP("[note: failure to get past this error may indicate source routing]\n");
++              DEBUGP("packet contains a bad checksum. [skip]\n");
++              return NF_ACCEPT;
++      }
++
++      /* perform direction dependant protocol work */
++      if (dir == IP_CT_DIR_ORIGINAL) {
++
++              DEBUGP("packet is from the initiator. [cont]\n");
++
++              /* Tests if packet len is ok */
++              if ((tcplen - (tcph->doff * 4)) != 60) {
++                      DEBUGP("packet length is not correct. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++      } else {
++
++              DEBUGP("packet is from the receiver. [cont]\n");
++
++              /* Tests if packet len is ok */
++              if ((tcplen - (tcph->doff * 4)) != 32) {
++                      DEBUGP("packet length is not correct. [skip]\n");
++                      return NF_ACCEPT;
++              }
++      }
++
++      /* Get to the data */
++      data++;
++
++      /* Check the RPC data */
++      crp_ret = check_rpc_packet(data, dir, ct, request_p_list_tcp);
++
++      return crp_ret;
++
++}
++
++
++static struct ip_conntrack_helper rpc_helpers[MAX_PORTS];
++
++static void fini(void);
++
++
++static int __init init(void)
++{
++      int port, ret;
++      static char name[10];
++
++
++      /* If no port given, default to standard RPC port */
++      if (ports[0] == 0)
++              ports[0] = RPC_PORT;
++
++      for (port = 0; (port < MAX_PORTS) && ports[port]; port++) {
++              memset(&rpc_helpers[port], 0, sizeof(struct ip_conntrack_helper));
++
++                if (ports[port] == RPC_PORT)
++                        sprintf(name, "rpc");
++                else
++                        sprintf(name, "rpc-%d", port);
++
++              rpc_helpers[port].name = name;
++              rpc_helpers[port].me = THIS_MODULE;
++              rpc_helpers[port].max_expected = 1;
++              rpc_helpers[port].flags = IP_CT_HELPER_F_REUSE_EXPECT;
++              rpc_helpers[port].timeout = 0;
++
++              rpc_helpers[port].tuple.dst.protonum = IPPROTO_TCP;
++              rpc_helpers[port].mask.dst.protonum = 0xffff;
++
++              /* RPC can come from ports 0:65535 to ports[port] (111) */
++              rpc_helpers[port].tuple.src.u.udp.port = htons(ports[port]);
++              rpc_helpers[port].mask.src.u.udp.port = htons(0xffff);
++              rpc_helpers[port].mask.dst.u.udp.port = htons(0x0);
++
++              rpc_helpers[port].help = help;
++
++              DEBUGP("registering helper for port #%d: %d/TCP\n", port, ports[port]);
++              DEBUGP("helper match ip   %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
++                      NIPQUAD(rpc_helpers[port].tuple.dst.ip),
++                      ntohs(rpc_helpers[port].tuple.dst.u.tcp.port),
++                      NIPQUAD(rpc_helpers[port].tuple.src.ip),
++                      ntohs(rpc_helpers[port].tuple.src.u.tcp.port));
++              DEBUGP("helper match mask %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
++                      NIPQUAD(rpc_helpers[port].mask.dst.ip),
++                      ntohs(rpc_helpers[port].mask.dst.u.tcp.port),
++                      NIPQUAD(rpc_helpers[port].mask.src.ip),
++                      ntohs(rpc_helpers[port].mask.src.u.tcp.port));
++
++              ret = ip_conntrack_helper_register(&rpc_helpers[port]);
++
++              if (ret) {
++                      printk("ERROR registering port %d\n",
++                              ports[port]);
++                      fini();
++                      return -EBUSY;
++              }
++              ports_n_c++;
++      }
++      return 0;
++}
++
++
++/* This function is intentionally _NOT_ defined as __exit, because 
++ * it is needed by the init function */
++static void fini(void)
++{
++      int port;
++
++      DEBUGP("cleaning request list\n");
++      clean_request(&request_p_list_tcp);
++
++      for (port = 0; (port < ports_n_c) && ports[port]; port++) {
++              DEBUGP("unregistering port %d\n", ports[port]);
++              ip_conntrack_helper_unregister(&rpc_helpers[port]);
++      }
++}
++
++
++module_init(init);
++module_exit(fini);
++
++struct module *ip_conntrack_rpc_tcp = THIS_MODULE;
++EXPORT_SYMBOL(request_p_list_tcp);
++EXPORT_SYMBOL(ip_conntrack_rpc_tcp);
++EXPORT_SYMBOL(ipct_rpc_tcp_lock);
++
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_rpc_udp.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_rpc_udp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_rpc_udp.c Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_rpc_udp.c     Wed Sep 24 09:18:01 2003
+@@ -0,0 +1,503 @@
++/* RPC extension for IP (UDP) connection tracking, Version 2.2
++ * (C) 2000 by Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>
++ *    - original rpc tracking module
++ *    - "recent" connection handling for kernel 2.3+ netfilter
++ *
++ * (C) 2001 by Rusty Russell <rusty@rustcorp.com.au>
++ *    - upgraded conntrack modules to oldnat api - kernel 2.4.0+
++ *
++ * (C) 2002,2003 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
++ *    - upgraded conntrack modules to newnat api - kernel 2.4.20+
++ *    - extended matching to support filtering on procedures
++ *
++ * ip_conntrack_rpc_udp.c,v 2.2 2003/01/12 18:30:00
++ *
++ *    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_rpc_udp.o ports=port1,port2,...port<MAX_PORTS>
++ *
++ *    Please give the ports of all RPC servers you wish to connect to.
++ *    If you don't specify ports, the default will be port 111.
++ **
++ *    Note to all:
++ *
++ *    RPCs should not be exposed to the internet - ask the Pentagon;
++ *
++ *      "The unidentified crackers pleaded guilty in July to charges
++ *       of juvenile delinquency stemming from a string of Pentagon
++ *       network intrusions in February.
++ *
++ *       The youths, going by the names TooShort and Makaveli, used
++ *       a common server security hole to break in, according to
++ *       Dane Jasper, owner of the California Internet service
++ *       provider, Sonic. They used the hole, known as the 'statd'
++ *       exploit, to attempt more than 800 break-ins, Jasper said."
++ *
++ *    From: Wired News; "Pentagon Kids Kicked Off Grid" - Nov 6, 1998
++ *    URL:  http://www.wired.com/news/politics/0,1283,16098,00.html
++ **
++ */
++
++#include <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++#include <net/udp.h>
++
++#include <asm/param.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/stddef.h>
++#include <linux/list.h>
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_rpc.h>
++
++#define MAX_PORTS 8
++static int ports[MAX_PORTS];
++static int ports_n_c = 0;
++
++#ifdef MODULE_PARM
++MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
++MODULE_PARM_DESC(ports, "port numbers (TCP/UDP) of RPC portmapper servers");
++#endif
++
++MODULE_AUTHOR("Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>");
++MODULE_DESCRIPTION("RPC UDP connection tracking module");
++MODULE_LICENSE("GPL");
++
++#if 0
++#define DEBUGP(format, args...) printk(KERN_DEBUG "ip_conntrack_rpc_udp: " \
++                                      format, ## args)
++#else
++#define DEBUGP(format, args...)
++#endif
++
++DECLARE_RWLOCK(ipct_rpc_udp_lock);
++#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock)
++#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++/* For future conections RPC, using client's cache bindings
++ * I'll use ip_conntrack_lock to lock these lists           */
++
++LIST_HEAD(request_p_list_udp);
++
++
++static void delete_request_p(unsigned long request_p_ul)
++{
++      struct request_p *p = (void *)request_p_ul;
++      
++      WRITE_LOCK(&ipct_rpc_udp_lock);
++      LIST_DELETE(&request_p_list_udp, p);
++      WRITE_UNLOCK(&ipct_rpc_udp_lock);
++      kfree(p);
++      return;
++}
++
++
++static void req_cl(struct request_p * r)
++{
++      WRITE_LOCK(&ipct_rpc_udp_lock);
++      del_timer(&r->timeout);
++      LIST_DELETE(&request_p_list_udp, r);
++      WRITE_UNLOCK(&ipct_rpc_udp_lock);
++      kfree(r);
++      return;
++}
++
++
++static void clean_request(struct list_head *list)
++{
++      struct list_head *first = list->prev;
++      struct list_head *temp = list->next;
++      struct list_head *aux;
++
++      if (list_empty(list))
++              return;
++
++      while (first != temp) {
++              aux = temp->next;
++              req_cl((struct request_p *)temp);
++              temp = aux;     
++      }
++      req_cl((struct request_p *)temp);
++      return;
++}
++
++
++static void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip,
++                   u_int16_t port)
++{
++      struct request_p *req_p;
++        
++      /* Verifies if entry already exists */
++      WRITE_LOCK(&ipct_rpc_udp_lock);
++      req_p = LIST_FIND(&request_p_list_udp, request_p_cmp,
++              struct request_p *, xid, ip, port);
++
++      if (req_p) {
++              /* Refresh timeout */
++              if (del_timer(&req_p->timeout)) {
++                      req_p->timeout.expires = jiffies + EXP;
++                      add_timer(&req_p->timeout);     
++              } 
++              WRITE_UNLOCK(&ipct_rpc_udp_lock);
++              return; 
++
++      }
++      WRITE_UNLOCK(&ipct_rpc_udp_lock);
++      
++      /* Allocate new request_p */
++      req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC);
++      if (!req_p) {
++              DEBUGP("can't allocate request_p\n");
++              return;                 
++      }
++      *req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, 
++              { { NULL, NULL }, jiffies + EXP, (unsigned long)req_p,
++                      NULL }}); 
++      
++      /* Initialize timer */
++      init_timer(&req_p->timeout);
++      req_p->timeout.function = delete_request_p;
++      add_timer(&req_p->timeout); 
++
++      /* Put in list */
++      WRITE_LOCK(&ipct_rpc_udp_lock);
++      list_prepend(&request_p_list_udp, req_p);
++      WRITE_UNLOCK(&ipct_rpc_udp_lock); 
++      return; 
++
++}
++
++
++static int check_rpc_packet(const u_int32_t *data,
++                      int dir, struct ip_conntrack *ct,
++                      struct list_head request_p_list)
++{
++      struct request_p *req_p;
++      u_int32_t xid;
++      struct ip_conntrack_expect expect, *exp = &expect;
++
++      /* Translstion's buffer for XDR */
++      u_int16_t port_buf;
++
++
++      /* Get XID */
++      xid = *data;
++
++      /* This does sanity checking on RPC payloads,
++       * and permits only the RPC "get port" (3)
++       * in authorised procedures in client
++       * communications with the portmapper.
++       */
++
++      /* perform direction dependant RPC work */
++      if (dir == IP_CT_DIR_ORIGINAL) {
++
++              data += 5;
++
++              /* Get RPC requestor */
++              if (IXDR_GET_INT32(data) != 3) {
++                      DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n");
++                      return NF_ACCEPT;
++              }
++              DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n");
++
++              data++;
++
++              /* Jump Credentials and Verfifier */
++              data = data + IXDR_GET_INT32(data) + 2;
++              data = data + IXDR_GET_INT32(data) + 2;
++
++              /* Get RPC procedure */
++              DEBUGP("RPC packet contains procedure request [%u]. [cont]\n",
++                      (unsigned int)IXDR_GET_INT32(data));
++
++              /* Get RPC protocol and store against client parameters */
++              data = data + 2;
++              alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip,
++                              ct->tuplehash[dir].tuple.src.u.all);
++
++              DEBUGP("allocated RPC req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n",
++                      xid, IXDR_GET_INT32(data),
++                      NIPQUAD(ct->tuplehash[dir].tuple.src.ip),
++                      ntohs(ct->tuplehash[dir].tuple.src.u.all));
++
++              DEBUGP("allocated RPC request for protocol %u. [done]\n",
++                      (unsigned int)IXDR_GET_INT32(data));
++
++      } else {
++
++              /* Check for returning packet's stored counterpart */
++              req_p = LIST_FIND(&request_p_list_udp, request_p_cmp,
++                                struct request_p *, xid,
++                                ct->tuplehash[!dir].tuple.src.ip,
++                                ct->tuplehash[!dir].tuple.src.u.all);
++
++              /* Drop unexpected packets */
++              if (!req_p) {
++                      DEBUGP("packet is not expected. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++              /* Verifies if packet is really an RPC reply packet */
++              data = data++;
++              if (IXDR_GET_INT32(data) != 1) {
++                      DEBUGP("packet is not a valid RPC reply. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++              /* Is status accept? */
++              data++;
++              if (IXDR_GET_INT32(data)) {
++                      DEBUGP("packet is not an RPC accept. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++              /* Get Verifier length. Jump verifier */
++              data++;
++              data = data + IXDR_GET_INT32(data) + 2;
++
++              /* Is accpet status "success"? */
++              if (IXDR_GET_INT32(data)) {
++                      DEBUGP("packet is not an RPC accept status of success. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++              /* Get server port number */      
++              data++;
++              port_buf = (u_int16_t) IXDR_GET_INT32(data);
++
++              /* If a packet has made it this far then it deserves an
++               * expectation ...  if port == 0, then this service is 
++               * not going to be registered.
++               */
++              if (port_buf) {
++                      DEBUGP("port found: %u\n", port_buf);
++
++                      memset(&expect, 0, sizeof(expect));
++
++                      /* Watch out, Radioactive-Man! */
++                      exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
++                      exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
++                      exp->mask.src.ip = 0xffffffff;
++                      exp->mask.dst.ip = 0xffffffff;
++
++                      switch (req_p->proto) {
++                              case IPPROTO_UDP:
++                                      exp->tuple.src.u.udp.port = 0;
++                                      exp->tuple.dst.u.udp.port = htons(port_buf);
++                                      exp->tuple.dst.protonum = IPPROTO_UDP;
++                                      exp->mask.src.u.udp.port = 0;
++                                      exp->mask.dst.u.udp.port = htons(0xffff);
++                                      exp->mask.dst.protonum = 0xffff;
++                                      break;
++
++                              case IPPROTO_TCP:
++                                      exp->tuple.src.u.tcp.port = 0;
++                                      exp->tuple.dst.u.tcp.port = htons(port_buf);
++                                      exp->tuple.dst.protonum = IPPROTO_TCP;
++                                      exp->mask.src.u.tcp.port = 0;
++                                      exp->mask.dst.u.tcp.port = htons(0xffff);
++                                      exp->mask.dst.protonum = 0xffff;
++                                      break;
++                      }
++                      exp->expectfn = NULL;
++
++                      ip_conntrack_expect_related(ct, &expect);
++
++                      DEBUGP("expect related ip   %u.%u.%u.%u:0-%u.%u.%u.%u:%u proto=%u\n",
++                              NIPQUAD(exp->tuple.src.ip),
++                              NIPQUAD(exp->tuple.dst.ip),
++                              port_buf, req_p->proto);
++
++                      DEBUGP("expect related mask %u.%u.%u.%u:0-%u.%u.%u.%u:65535 proto=%u\n",
++                              NIPQUAD(exp->mask.src.ip),
++                              NIPQUAD(exp->mask.dst.ip),
++                              exp->mask.dst.protonum);
++
++              }
++
++              req_cl(req_p);
++
++              DEBUGP("packet evaluated. [expect]\n");
++              return NF_ACCEPT;
++      }
++
++      return NF_ACCEPT;
++
++}
++
++
++/* RPC UDP helper */
++static int help(const struct iphdr *iph, size_t len,
++              struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
++{
++      struct udphdr *udph = (void *) iph + iph->ihl * 4;
++      const u_int32_t *data = (const u_int32_t *)udph + 2;
++      size_t udplen = len - iph->ihl * 4;
++      int dir = CTINFO2DIR(ctinfo);
++      int crp_ret;
++
++      /* Checksum */
++      const u_int16_t *chsm = (const u_int16_t *)udph + 3;
++
++
++      DEBUGP("new packet to evaluate ..\n");
++
++      /* Not whole UDP header? */
++      if (udplen < sizeof(struct udphdr)) {
++              DEBUGP("UDP header length is; udplen=%u ..\n", (unsigned) udplen);
++              DEBUGP("packet does not contain a complete UDP header. [skip]\n");
++              return NF_ACCEPT;
++      }
++
++      /* FIXME: Source route IP option packets --RR */
++      if (*chsm) {
++              if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP,
++                  csum_partial((char *)udph, udplen, 0))) {
++                      DEBUGP("[note: failure to get past this error may indicate source routing]\n");
++                      DEBUGP("packet contains a bad checksum. [skip]\n");
++                      return NF_ACCEPT;
++                 } 
++      }
++
++      /* perform direction dependant protocol work */
++      if (dir == IP_CT_DIR_ORIGINAL) {
++
++              DEBUGP("packet is from the initiator. [cont]\n");
++
++              /* Tests if packet len is ok */
++              if ((udplen - sizeof(struct udphdr)) != 56) {
++                      DEBUGP("packet length is not correct. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++      } else {
++
++              DEBUGP("packet is from the receiver. [cont]\n");
++
++              /* Until there's been traffic both ways, don't look in packets. */
++              if (ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
++                      DEBUGP("connection tracking state is; ctinfo=%u ..\n", ctinfo);
++                      DEBUGP("[note: failure to get past this error may indicate asymmetric routing]\n");
++                      DEBUGP("packet is not yet part of a two way stream. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++              /* Tests if packet len is ok */
++                      if ((udplen - sizeof(struct udphdr)) != 28) {
++                      DEBUGP("packet length is not correct. [skip]\n");
++                      return NF_ACCEPT;
++              }
++
++      }
++
++      /* Get to the data */
++      /* udp *data == *correct */
++
++      /* Check the RPC data */
++      crp_ret = check_rpc_packet(data, dir, ct, request_p_list_udp);
++
++      return crp_ret;
++
++}
++
++
++static struct ip_conntrack_helper rpc_helpers[MAX_PORTS];
++
++static void fini(void);
++
++
++static int __init init(void)
++{
++      int port, ret;
++      static char name[10];
++
++
++      /* If no port given, default to standard RPC port */
++      if (ports[0] == 0)
++              ports[0] = RPC_PORT;
++
++      for (port = 0; (port < MAX_PORTS) && ports[port]; port++) {
++              memset(&rpc_helpers[port], 0, sizeof(struct ip_conntrack_helper));
++
++                if (ports[port] == RPC_PORT)
++                        sprintf(name, "rpc");
++                else
++                        sprintf(name, "rpc-%d", port);
++
++              rpc_helpers[port].name = name;
++              rpc_helpers[port].me = THIS_MODULE;
++              rpc_helpers[port].max_expected = 1;
++              rpc_helpers[port].flags = IP_CT_HELPER_F_REUSE_EXPECT;
++              rpc_helpers[port].timeout = 0;
++
++              rpc_helpers[port].tuple.dst.protonum = IPPROTO_UDP;
++              rpc_helpers[port].mask.dst.protonum = 0xffff;
++
++              /* RPC can come from ports 0:65535 to ports[port] (111) */
++              rpc_helpers[port].tuple.src.u.udp.port = htons(ports[port]);
++              rpc_helpers[port].mask.src.u.udp.port = htons(0xffff);
++              rpc_helpers[port].mask.dst.u.udp.port = htons(0x0);
++
++              rpc_helpers[port].help = help;
++
++              DEBUGP("registering helper for port #%d: %d/UDP\n", port, ports[port]);
++              DEBUGP("helper match ip   %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
++                      NIPQUAD(rpc_helpers[port].tuple.dst.ip),
++                      ntohs(rpc_helpers[port].tuple.dst.u.udp.port),
++                      NIPQUAD(rpc_helpers[port].tuple.src.ip),
++                      ntohs(rpc_helpers[port].tuple.src.u.udp.port));
++              DEBUGP("helper match mask %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
++                      NIPQUAD(rpc_helpers[port].mask.dst.ip),
++                      ntohs(rpc_helpers[port].mask.dst.u.udp.port),
++                      NIPQUAD(rpc_helpers[port].mask.src.ip),
++                      ntohs(rpc_helpers[port].mask.src.u.udp.port));
++
++              ret = ip_conntrack_helper_register(&rpc_helpers[port]);
++
++              if (ret) {
++                      printk("ERROR registering port %d\n",
++                              ports[port]);
++                      fini();
++                      return -EBUSY;
++              }
++              ports_n_c++;
++      }
++      return 0;
++}
++
++
++/* This function is intentionally _NOT_ defined as __exit, because 
++ * it is needed by the init function */
++static void fini(void)
++{
++      int port;
++
++      DEBUGP("cleaning request list\n");
++      clean_request(&request_p_list_udp);
++
++      for (port = 0; (port < ports_n_c) && ports[port]; port++) {
++              DEBUGP("unregistering port %d\n", ports[port]);
++              ip_conntrack_helper_unregister(&rpc_helpers[port]);
++      }
++}
++
++
++module_init(init);
++module_exit(fini);
++
++struct module *ip_conntrack_rpc_udp = THIS_MODULE;
++EXPORT_SYMBOL(request_p_list_udp);
++EXPORT_SYMBOL(ip_conntrack_rpc_udp);
++EXPORT_SYMBOL(ipct_rpc_udp_lock);
++
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_rsh.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_rsh.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_rsh.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_rsh.c Wed Sep 24 09:18:03 2003
+@@ -0,0 +1,331 @@
++/* RSH extension for IP connection tracking, Version 1.0
++ * (C) 2002 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
++ * based on HW's ip_conntrack_irc.c   
++ *
++ * ip_conntrack_rsh.c,v 1.0 2002/07/17 14:49:26
++ *
++ *      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_rsh.o ports=port1,port2,...port<MAX_PORTS>
++ *    
++ *    please give the ports of all RSH servers You wish to connect to.
++ *    If You don't specify ports, the default will be port 514
++ **
++ *      Note to all:
++ *        RSH blows ... you should use SSH (openssh.org) to replace it,
++ *        unfortunately I babysit some sysadmins that won't migrate
++ *      their legacy crap, in our second tier.
++ */
++
++
++/*
++ *  Some docco ripped from the net to teach me all there is to know about
++ *  RSH, in 16.5 seconds (ie, all of the non-netfilter docco used to write
++ *  this module).
++ *
++ *  I have no idea what "unix rshd man pages" these guys have .. but that
++ *  is some pretty detailed docco!
++ **
++ *
++ *  4. Of the rsh protocol.
++ *  -----------------------
++ * 
++ *   The rshd listens on TCP port #514. The following info is from the unix
++ *   rshd man pages :
++ * 
++ *   "Service Request Protocol
++ * 
++ *    When the rshd daemon receives a service request, it initiates the
++ *    following protocol:
++ * 
++ *     1. The rshd daemon checks the source port number for the request.
++ *        If the port number is not in the range 0 through 1023, the rshd daemon
++ *        terminates the connection.
++ * 
++ *     2. The rshd daemon reads characters from the socket up to a null byte.
++ *        The string read is interpreted as an ASCII number (base 10). If this
++ *        number is nonzero, the rshd daemon interprets it as the port number
++ *        of a secondary stream to be used as standard error. A second connection
++ *        is created to the specified port on the client host. The source port
++ *        on the local host is in the range 0 through 1023.
++ * 
++ *     3. The rshd daemon uses the source address of the initial connection
++ *        request to determine the name of the client host. If the name cannot
++ *        be determined, the rshd daemon uses the dotted decimal representation
++ *        of the client host's address.
++ * 
++ *     4. The rshd daemon retrieves the following information from the initial
++ *        socket:
++ * 
++ *         * A null-terminated string of at most 16 bytes interpreted as
++ *           the user name of the user on the client host.
++ * 
++ *         * A null-terminated string of at most 16 bytes interpreted as
++ *           the user name to be used on the local server host.
++ * 
++ *         * Another null-terminated string interpreted as a command line
++ *           to be passed to a shell on the local server host.
++ * 
++ *     5. The rshd daemon attempts to validate the user using the following steps:
++ * 
++ *         a. The rshd daemon looks up the local user name in the /etc/passwd
++ *            file and tries to switch to the home directory (using the chdir
++ *            subroutine). If either the lookup or the directory change fails,
++ *            the rshd daemon terminates the connection.
++ * 
++ *         b. If the local user ID is a nonzero value, the rshd daemon searches
++ *            the /etc/hosts.equiv file to see if the name of the client
++ *            workstation is listed. If the client workstation is listed as an
++ *            equivalent host, the rshd daemon validates the user.
++ * 
++ *         c. If the $HOME/.rhosts file exists, the rshd daemon tries to
++ *            authenticate the user by checking the .rhosts file.
++ * 
++ *         d. If either the $HOME/.rhosts authentication fails or the
++ *            client host is not an equivalent host, the rshd daemon
++ *            terminates the connection.
++ * 
++ *     6. Once rshd validates the user, the rshd daemon returns a null byte
++ *        on the initial connection and passes the command line to the user's
++ *        local login shell. The shell then inherits the network connections
++ *        established by the rshd daemon."
++ * 
++ */
++
++
++#include <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++#include <net/tcp.h>
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_rsh.h>
++
++#define MAX_PORTS 8
++static int ports[MAX_PORTS];
++static int ports_n_c = 0;
++
++MODULE_AUTHOR("Ian (Larry) Latter <Ian.Latter@mq.edu.au>");
++MODULE_DESCRIPTION("RSH connection tracking module");
++MODULE_LICENSE("GPL");
++#ifdef MODULE_PARM
++MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
++MODULE_PARM_DESC(ports, "port numbers of RSH servers");
++#endif
++
++DECLARE_LOCK(ip_rsh_lock);
++struct module *ip_conntrack_rsh = THIS_MODULE;
++
++#if 0
++#define DEBUGP(format, args...) printk(KERN_DEBUG "ip_conntrack_rsh: " \
++                                      format, ## args)
++#else
++#define DEBUGP(format, args...)
++#endif
++
++
++
++/* FIXME: This should be in userspace.  Later. */
++static int help(const struct iphdr *iph, size_t len,
++              struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
++{
++      /* tcplen not negative guarenteed by ip_conntrack_tcp.c */
++      struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
++      const char *data = (const char *) tcph + tcph->doff * 4;
++      u_int32_t tcplen = len - iph->ihl * 4;
++      int dir = CTINFO2DIR(ctinfo);
++        struct ip_conntrack_expect expect, *exp = &expect;
++        struct ip_ct_rsh_expect *exp_rsh_info = &exp->help.exp_rsh_info;
++      u_int16_t port;
++      int maxoctet;
++
++      /*  note that "maxoctet" is used to maintain sanity (8 was the
++       *  original array size used in rshd/glibc) -- is there a
++       *  vulnerability in rshd.c in the looped port *= 10?
++       */
++
++
++      DEBUGP("entered\n");
++
++      /* bail if packet is not from RSH client */
++      if (dir == IP_CT_DIR_REPLY)
++              return NF_ACCEPT;
++
++      /* Until there's been traffic both ways, don't look in packets. */
++      if (ctinfo != IP_CT_ESTABLISHED
++          && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
++              DEBUGP("Conntrackinfo = %u\n", ctinfo);
++              return NF_ACCEPT;
++      }
++
++      /* Not whole TCP header? */
++      if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
++              DEBUGP("tcplen = %u\n", (unsigned) tcplen);
++              return NF_ACCEPT;
++      }
++
++      /* Checksum invalid?  Ignore. */
++      /* FIXME: Source route IP option packets --RR */
++      if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
++                       csum_partial((char *) tcph, tcplen, 0))) {
++              DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
++                   tcph, tcplen, NIPQUAD(iph->saddr),
++                   NIPQUAD(iph->daddr));
++              return NF_ACCEPT;
++      }
++
++      /* find the rsh stderr port */
++      maxoctet = 4;
++      port = 0;
++      for ( ; *data != 0 && maxoctet != 0; data++, maxoctet--) {
++              if (*data < 0)
++                      return(1);
++              if (*data == 0)
++                      break;
++              if (*data < 48 || *data > 57) {
++                      DEBUGP("these aren't the packets you're looking for ..\n");
++                      return NF_ACCEPT;
++              }
++              port = port * 10 + ( *data - 48 );
++      }
++
++      /* dont relate sessions that try to expose the client */
++      DEBUGP("found port %u\n", port);
++      if (port > 1023) {
++              DEBUGP("skipping, expected port size is greater than 1023!\n");
++              return NF_ACCEPT;
++      }
++
++
++      LOCK_BH(&ip_rsh_lock);
++
++      /*  new(,related) connection is;
++       *          reply + dst (uint)port + src port (0:1023)
++       */
++      memset(&expect, 0, sizeof(expect));
++
++      /*  save some discovered data, in case someone ever wants to write
++       *  a NAT module for this bastard ..
++       */
++      exp_rsh_info->port = port;
++
++      DEBUGP("wrote info port=%u\n", exp_rsh_info->port);
++
++
++      /* Watch out, Radioactive-Man! */
++      exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
++      exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
++      exp->tuple.src.u.tcp.port = 0;
++      exp->tuple.dst.u.tcp.port = htons(exp_rsh_info->port);
++      exp->tuple.dst.protonum = IPPROTO_TCP;
++
++      exp->mask.src.ip = 0xffffffff;
++      exp->mask.dst.ip = 0xffffffff;
++
++      exp->mask.src.u.tcp.port = htons(0xfc00);
++      exp->mask.dst.u.tcp.port = htons(0xfc00);
++      exp->mask.dst.protonum = 0xffff;
++
++      exp->expectfn = NULL;
++
++      ip_conntrack_expect_related(ct, &expect);
++
++      DEBUGP("expect related ip   %u.%u.%u.%u:%u-%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));
++
++      DEBUGP("expect related mask %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
++              NIPQUAD(exp->mask.src.ip),
++              ntohs(exp->mask.src.u.tcp.port),
++              NIPQUAD(exp->mask.dst.ip),
++              ntohs(exp->mask.dst.u.tcp.port));
++      UNLOCK_BH(&ip_rsh_lock);
++
++      return NF_ACCEPT;
++}
++
++static struct ip_conntrack_helper rsh_helpers[MAX_PORTS];
++
++static void fini(void);
++
++static int __init init(void)
++{
++      int port, ret;
++      static char name[10];
++
++
++      /* If no port given, default to standard RSH port */
++      if (ports[0] == 0)
++              ports[0] = RSH_PORT;
++
++      for (port = 0; (port < MAX_PORTS) && ports[port]; port++) {
++              memset(&rsh_helpers[port], 0, sizeof(struct ip_conntrack_helper));
++
++                if (ports[port] == RSH_PORT)
++                        sprintf(name, "rsh");
++                else
++                        sprintf(name, "rsh-%d", port);
++
++              rsh_helpers[port].name = name;
++              rsh_helpers[port].me = THIS_MODULE;
++              rsh_helpers[port].max_expected = 1;
++              rsh_helpers[port].flags = IP_CT_HELPER_F_REUSE_EXPECT;
++              rsh_helpers[port].timeout = 0;
++
++              rsh_helpers[port].tuple.dst.protonum = IPPROTO_TCP;
++              rsh_helpers[port].mask.dst.protonum = 0xffff;
++
++              /* RSH must come from ports 0:1023 to ports[port] (514) */
++              rsh_helpers[port].tuple.src.u.tcp.port = htons(ports[port]);
++              rsh_helpers[port].mask.src.u.tcp.port = htons(0xfc00);
++              rsh_helpers[port].mask.dst.u.tcp.port = htons(0xfc00);
++
++              rsh_helpers[port].help = help;
++
++              DEBUGP("registering helper for port #%d: %d/TCP\n", port, ports[port]);
++              DEBUGP("helper match ip   %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
++                      NIPQUAD(rsh_helpers[port].tuple.src.ip),
++                      ntohs(rsh_helpers[port].tuple.src.u.tcp.port),
++                      NIPQUAD(rsh_helpers[port].tuple.dst.ip),
++                      ntohs(rsh_helpers[port].tuple.dst.u.tcp.port));
++              DEBUGP("helper match mask %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
++                      NIPQUAD(rsh_helpers[port].mask.src.ip),
++                      ntohs(rsh_helpers[port].mask.src.u.tcp.port),
++                      NIPQUAD(rsh_helpers[port].mask.dst.ip),
++                      ntohs(rsh_helpers[port].mask.dst.u.tcp.port));
++
++              ret = ip_conntrack_helper_register(&rsh_helpers[port]);
++
++              if (ret) {
++                      printk("ERROR registering port %d\n",
++                              ports[port]);
++                      fini();
++                      return -EBUSY;
++              }
++              ports_n_c++;
++      }
++      return 0;
++}
++
++/* This function is intentionally _NOT_ defined as __exit, because 
++ * it is needed by the init function */
++static void fini(void)
++{
++      int port;
++      for (port = 0; (port < MAX_PORTS) && ports[port]; port++) {
++              DEBUGP("unregistering port %d\n", ports[port]);
++              ip_conntrack_helper_unregister(&rsh_helpers[port]);
++      }
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_standalone.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_standalone.c      Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_standalone.c  Wed Sep 24 09:18:16 2003
+@@ -7,6 +7,7 @@
+ /* (c) 1999 Paul `Rusty' Russell.  Licenced under the GNU General
+    Public Licence. */
++#include <linux/config.h>
+ #include <linux/types.h>
+ #include <linux/ip.h>
+ #include <linux/netfilter.h>
+@@ -16,6 +17,9 @@
+ #include <linux/proc_fs.h>
+ #include <linux/version.h>
+ #include <linux/brlock.h>
++#ifdef CONFIG_SYSCTL
++#include <linux/sysctl.h>
++#endif
+ #include <net/checksum.h>
+ #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
+@@ -77,7 +81,7 @@
+ }
+ static unsigned int
+-print_conntrack(char *buffer, const struct ip_conntrack *conntrack)
++print_conntrack(char *buffer, struct ip_conntrack *conntrack)
+ {
+       unsigned int len;
+       struct ip_conntrack_protocol *proto
+@@ -95,15 +99,18 @@
+       len += print_tuple(buffer + len,
+                          &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
+                          proto);
+-      if (!(conntrack->status & IPS_SEEN_REPLY))
++      if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
+               len += sprintf(buffer + len, "[UNREPLIED] ");
+       len += print_tuple(buffer + len,
+                          &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
+                          proto);
+-      if (conntrack->status & IPS_ASSURED)
++      if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
+               len += sprintf(buffer + len, "[ASSURED] ");
+       len += sprintf(buffer + len, "use=%u ",
+                      atomic_read(&conntrack->ct_general.use));
++#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
++      len += sprintf(buffer + len, "mark=%ld ", conntrack->mark);
++#endif
+       len += sprintf(buffer + len, "\n");
+       return len;
+@@ -239,6 +246,98 @@
+ static struct nf_hook_ops ip_conntrack_local_in_ops
+ = { { NULL, NULL }, ip_confirm, PF_INET, NF_IP_LOCAL_IN, NF_IP_PRI_LAST-1 };
++/* Sysctl support */
++
++#ifdef CONFIG_SYSCTL
++
++/* From ip_conntrack_core.c */
++extern int ip_conntrack_max;
++
++/* From ip_conntrack_proto_tcp.c */
++extern unsigned long ip_ct_tcp_timeout_syn_sent;
++extern unsigned long ip_ct_tcp_timeout_syn_recv;
++extern unsigned long ip_ct_tcp_timeout_established;
++extern unsigned long ip_ct_tcp_timeout_fin_wait;
++extern unsigned long ip_ct_tcp_timeout_close_wait;
++extern unsigned long ip_ct_tcp_timeout_last_ack;
++extern unsigned long ip_ct_tcp_timeout_time_wait;
++extern unsigned long ip_ct_tcp_timeout_close;
++
++/* From ip_conntrack_proto_udp.c */
++extern unsigned long ip_ct_udp_timeout;
++extern unsigned long ip_ct_udp_timeout_stream;
++
++/* From ip_conntrack_proto_icmp.c */
++extern unsigned long ip_ct_icmp_timeout;
++
++/* From ip_conntrack_proto_icmp.c */
++extern unsigned long ip_ct_generic_timeout;
++
++static struct ctl_table_header *ip_ct_sysctl_header;
++
++static ctl_table ip_ct_sysctl_table[] = {
++      {NET_IPV4_NF_CONNTRACK_MAX, "ip_conntrack_max",
++       &ip_conntrack_max, sizeof(int), 0644, NULL,
++       &proc_dointvec},
++      {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT, "ip_conntrack_tcp_timeout_syn_sent",
++       &ip_ct_tcp_timeout_syn_sent, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV, "ip_conntrack_tcp_timeout_syn_recv",
++       &ip_ct_tcp_timeout_syn_recv, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED, "ip_conntrack_tcp_timeout_established",
++       &ip_ct_tcp_timeout_established, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT, "ip_conntrack_tcp_timeout_fin_wait",
++       &ip_ct_tcp_timeout_fin_wait, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT, "ip_conntrack_tcp_timeout_close_wait",
++       &ip_ct_tcp_timeout_close_wait, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK, "ip_conntrack_tcp_timeout_last_ack",
++       &ip_ct_tcp_timeout_last_ack, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT, "ip_conntrack_tcp_timeout_time_wait",
++       &ip_ct_tcp_timeout_time_wait, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE, "ip_conntrack_tcp_timeout_close",
++       &ip_ct_tcp_timeout_close, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT, "ip_conntrack_udp_timeout",
++       &ip_ct_udp_timeout, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT_STREAM, "ip_conntrack_udp_timeout_stream",
++       &ip_ct_udp_timeout_stream, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT, "ip_conntrack_icmp_timeout",
++       &ip_ct_icmp_timeout, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT, "ip_conntrack_generic_timeout",
++       &ip_ct_generic_timeout, sizeof(unsigned int), 0644, NULL,
++       &proc_dointvec_jiffies},
++      {0}
++};
++
++#define NET_IP_CONNTRACK_MAX 2089
++
++static ctl_table ip_ct_netfilter_table[] = {
++      {NET_IPV4_NETFILTER, "netfilter", NULL, 0, 0555, ip_ct_sysctl_table, 0, 0, 0, 0, 0},
++      {NET_IP_CONNTRACK_MAX, "ip_conntrack_max",
++       &ip_conntrack_max, sizeof(int), 0644, NULL,
++       &proc_dointvec},
++      {0}
++};
++
++static ctl_table ip_ct_ipv4_table[] = {
++      {NET_IPV4, "ipv4", NULL, 0, 0555, ip_ct_netfilter_table, 0, 0, 0, 0, 0},
++      {0}
++};
++
++static ctl_table ip_ct_net_table[] = {
++      {CTL_NET, "net", NULL, 0, 0555, ip_ct_ipv4_table, 0, 0, 0, 0, 0},
++      {0}
++};
++#endif
+ static int init_or_cleanup(int init)
+ {
+       struct proc_dir_entry *proc;
+@@ -274,10 +373,20 @@
+               printk("ip_conntrack: can't register local in hook.\n");
+               goto cleanup_inoutandlocalops;
+       }
++#ifdef CONFIG_SYSCTL
++      ip_ct_sysctl_header = register_sysctl_table(ip_ct_net_table, 0);
++      if (ip_ct_sysctl_header == NULL) {
++              printk("ip_conntrack: can't register to sysctl.\n");
++              goto cleanup;
++      }
++#endif
+       return ret;
+  cleanup:
++#ifdef CONFIG_SYSCTL
++      unregister_sysctl_table(ip_ct_sysctl_header);
++#endif
+       nf_unregister_hook(&ip_conntrack_local_in_ops);
+  cleanup_inoutandlocalops:
+       nf_unregister_hook(&ip_conntrack_out_ops);
+@@ -293,13 +402,20 @@
+       return ret;
+ }
+-/* FIXME: Allow NULL functions and sub in pointers to generic for
+-   them. --RR */
++/**
++ * ip_conntrack_protocol_register - Register layer 4 protocol helper
++ * @proto: structure describing this layer 4 protocol helper
++ *
++ * This function is called by layer 4 protocol helpers to register 
++ * themselves with the conntrack core.
++ */
+ int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto)
+ {
+       int ret = 0;
+       struct list_head *i;
++      /* FIXME: Allow NULL functions and sub in pointers to generic for
++         them. --RR */
+       WRITE_LOCK(&ip_conntrack_lock);
+       for (i = protocol_list.next; i != &protocol_list; i = i->next) {
+               if (((struct ip_conntrack_protocol *)i)->proto
+@@ -317,12 +433,20 @@
+       return ret;
+ }
++/**
++ * ip_conntrack_protocol_unregister - Unregister layer 4 protocol helper
++ * @proto: structure describing this layer 4 protocol helper
++ *
++ * This function is called byh layer 4 protocol helpers to unregister
++ * themselvers from the conntrack core.  Please note that all conntrack
++ * entries for this protocol are deleted from the conntrack hash table.
++ */
+ void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto)
+ {
+       WRITE_LOCK(&ip_conntrack_lock);
+-      /* ip_ct_find_proto() returns proto_generic in case there is no protocol 
+-       * helper. So this should be enough - HW */
++      /* ip_ct_find_proto() returns proto_generic in case there is no
++       * protocol helper. So this should be enough - HW */
+       LIST_DELETE(&protocol_list, proto);
+       WRITE_UNLOCK(&ip_conntrack_lock);
+       
+@@ -355,11 +479,13 @@
+ EXPORT_SYMBOL(ip_conntrack_alter_reply);
+ EXPORT_SYMBOL(ip_conntrack_destroyed);
+ EXPORT_SYMBOL(ip_conntrack_get);
++EXPORT_SYMBOL(ip_conntrack_get_tuple);
+ EXPORT_SYMBOL(ip_conntrack_module);
+ EXPORT_SYMBOL(ip_conntrack_helper_register);
+ EXPORT_SYMBOL(ip_conntrack_helper_unregister);
+ EXPORT_SYMBOL(ip_ct_selective_cleanup);
+ EXPORT_SYMBOL(ip_ct_refresh);
++EXPORT_SYMBOL(ip_ct_death_by_timeout);
+ EXPORT_SYMBOL(ip_ct_find_proto);
+ EXPORT_SYMBOL(__ip_ct_find_proto);
+ EXPORT_SYMBOL(ip_ct_find_helper);
+@@ -374,5 +500,6 @@
+ EXPORT_SYMBOL(ip_conntrack_expect_list);
+ EXPORT_SYMBOL(ip_conntrack_lock);
+ EXPORT_SYMBOL(ip_conntrack_hash);
++EXPORT_SYMBOL(ip_conntrack_untracked);
+ EXPORT_SYMBOL_GPL(ip_conntrack_find_get);
+ EXPORT_SYMBOL_GPL(ip_conntrack_put);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_talk.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_talk.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_talk.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_talk.c        Wed Sep 24 09:18:08 2003
+@@ -0,0 +1,360 @@
++/* 
++ * talk extension for IP connection tracking. 
++ * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ **
++ *     Module load syntax:
++ *     insmod ip_nat_talk.o talk=[0|1] ntalk=[0|1] ntalk2=[01]
++ *
++ *            talk=[0|1]      disable|enable old talk support
++ *           ntalk=[0|1]      disable|enable ntalk support
++ *          ntalk2=[0|1]      disable|enable ntalk2 support
++ *
++ *     The default is talk=1 ntalk=1 ntalk2=1
++ *
++ *     The helper does not support simultaneous talk requests.
++ **
++ *
++ *            ASCII art on talk protocols
++ *    
++ *    
++ *    caller server               callee server
++ *            |     \           /
++ *            |       \       /
++ *            |         \   /
++ *            |           /  
++ *            |         /   \
++ *          2 |     1 /       \ 3
++ *    caller client  ----------- callee client
++ *                             4
++ *
++ *    1. caller client <-> callee server: LOOK_UP, then ANNOUNCE invitation 
++ *    ( 2. caller client <-> caller server: LEAVE_INVITE to server )
++ *    3. callee client <-> caller server: LOOK_UP invitation
++ *    4. callee client <-> caller client: talk data channel
++ *
++ * [1]: M. Hunter, talk: a historical protocol for interactive communication
++ *      draft-hunter-talk-00.txt
++ * [2]: D.B. Chapman, E.D. Zwicky: Building Internet Firewalls (O'Reilly)     
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++#include <net/udp.h>
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++#include <linux/netfilter_ipv4/ip_conntrack_core.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_talk.h>
++
++/* Default all talk protocols are supported */
++static int talk = 1;
++static int ntalk = 1;
++static int ntalk2 = 1;
++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
++MODULE_DESCRIPTION("talk connection tracking module");
++MODULE_LICENSE("GPL");
++#ifdef MODULE_PARM
++MODULE_PARM(talk, "i");
++MODULE_PARM_DESC(talk, "support (old) talk protocol");
++MODULE_PARM(ntalk, "i");
++MODULE_PARM_DESC(ntalk, "support ntalk protocol");
++MODULE_PARM(ntalk2, "i");
++MODULE_PARM_DESC(ntalk2, "support ntalk2 protocol");
++#endif
++
++DECLARE_LOCK(ip_talk_lock);
++struct module *ip_conntrack_talk = THIS_MODULE;
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++static int talk_expect(struct ip_conntrack *ct);
++static int ntalk_expect(struct ip_conntrack *ct);
++
++static int (*talk_expectfn[2])(struct ip_conntrack *ct) = {talk_expect, ntalk_expect};
++
++static int talk_help_response(const struct iphdr *iph, size_t len,
++                            struct ip_conntrack *ct,
++                            enum ip_conntrack_info ctinfo,
++                            int talk_port,
++                            u_char mode,
++                            u_char type,
++                            u_char answer,
++                            struct talk_addr *addr)
++{
++      int dir = CTINFO2DIR(ctinfo);
++      struct ip_conntrack_expect expect, *exp = &expect;
++      struct ip_ct_talk_expect *exp_talk_info = &exp->help.exp_talk_info;
++
++      DEBUGP("ip_ct_talk_help_response: %u.%u.%u.%u:%u, type %d answer %d\n",
++              NIPQUAD(addr->ta_addr), ntohs(addr->ta_port),
++              type, answer);
++
++      if (!(answer == SUCCESS && type == mode))
++              return NF_ACCEPT;
++      
++      memset(&expect, 0, sizeof(expect));
++      
++      if (type == ANNOUNCE) {
++
++              DEBUGP("ip_ct_talk_help_response: ANNOUNCE\n");
++
++              /* update the talk info */
++              LOCK_BH(&ip_talk_lock);
++              exp_talk_info->port = htons(talk_port);
++
++              /* expect callee client -> caller server message */
++              exp->tuple = ((struct ip_conntrack_tuple)
++                      { { ct->tuplehash[dir].tuple.src.ip,
++                          { 0 } },
++                        { ct->tuplehash[dir].tuple.dst.ip,
++                          { .tcp = { htons(talk_port) } },
++                          IPPROTO_UDP }});
++              exp->mask = ((struct ip_conntrack_tuple)
++                      { { 0xFFFFFFFF, { 0 } },
++                        { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
++              
++              exp->expectfn = talk_expectfn[talk_port - TALK_PORT];
++
++              DEBUGP("ip_ct_talk_help_response: callee client %u.%u.%u.%u:%u -> caller daemon %u.%u.%u.%u:%u!\n",
++                     NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.udp.port),
++                     NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.udp.port));
++
++              /* Ignore failure; should only happen with NAT */
++              ip_conntrack_expect_related(ct, &expect);
++              UNLOCK_BH(&ip_talk_lock);
++      }
++      if (type == LOOK_UP) {
++
++              DEBUGP("ip_ct_talk_help_response: LOOK_UP\n");
++
++              /* update the talk info */
++              LOCK_BH(&ip_talk_lock);
++              exp_talk_info->port = addr->ta_port;
++
++              /* expect callee client -> caller client connection */
++              exp->tuple = ((struct ip_conntrack_tuple)
++                      { { ct->tuplehash[!dir].tuple.src.ip,
++                          { 0 } },
++                        { addr->ta_addr,
++                          { addr->ta_port },
++                          IPPROTO_TCP }});
++              exp->mask = ((struct ip_conntrack_tuple)
++                      { { 0xFFFFFFFF, { 0 } },
++                        { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
++              
++              exp->expectfn = NULL;
++              
++              DEBUGP("ip_ct_talk_help_response: callee client %u.%u.%u.%u:%u -> caller client %u.%u.%u.%u:%u!\n",
++                     NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port),
++                     NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port));
++
++              /* Ignore failure; should only happen with NAT */
++              ip_conntrack_expect_related(ct, &expect);
++              UNLOCK_BH(&ip_talk_lock);
++      }
++                  
++      return NF_ACCEPT;
++}
++
++/* FIXME: This should be in userspace.  Later. */
++static int talk_help(const struct iphdr *iph, size_t len,
++                   struct ip_conntrack *ct,
++                   enum ip_conntrack_info ctinfo,
++                   int talk_port,
++                   u_char mode)
++{
++      struct udphdr *udph = (void *)iph + iph->ihl * 4;
++      const char *data = (const char *)udph + sizeof(struct udphdr);
++      int dir = CTINFO2DIR(ctinfo);
++      size_t udplen;
++
++      DEBUGP("ip_ct_talk_help: help entered\n");
++
++      /* Until there's been traffic both ways, don't look in packets. */
++      if (ctinfo != IP_CT_ESTABLISHED
++          && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
++              DEBUGP("ip_ct_talk_help: Conntrackinfo = %u\n", ctinfo);
++              return NF_ACCEPT;
++      }
++
++      /* Not whole UDP header? */
++      udplen = len - iph->ihl * 4;
++      if (udplen < sizeof(struct udphdr)) {
++              DEBUGP("ip_ct_talk_help: too short for udph, udplen = %u\n", (unsigned)udplen);
++              return NF_ACCEPT;
++      }
++
++      /* Checksum invalid?  Ignore. */
++      /* FIXME: Source route IP option packets --RR */
++      if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP,
++                            csum_partial((char *)udph, udplen, 0))) {
++              DEBUGP("ip_ct_talk_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
++                     udph, udplen, NIPQUAD(iph->saddr),
++                     NIPQUAD(iph->daddr));
++              return NF_ACCEPT;
++      }
++      
++      DEBUGP("ip_ct_talk_help: %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
++              NIPQUAD(iph->saddr), ntohs(udph->source), NIPQUAD(iph->daddr), ntohs(udph->dest));
++
++      if (dir == IP_CT_DIR_ORIGINAL)
++              return NF_ACCEPT;
++              
++      if (talk_port == TALK_PORT
++          && udplen == sizeof(struct udphdr) + sizeof(struct talk_response))
++              return talk_help_response(iph, len, ct, ctinfo, talk_port, mode,
++                                        ((struct talk_response *)data)->type, 
++                                        ((struct talk_response *)data)->answer,
++                                        &(((struct talk_response *)data)->addr));
++      else if (talk_port == NTALK_PORT
++                && ntalk
++                && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_response)
++                && ((struct ntalk_response *)data)->vers == NTALK_VERSION)
++              return talk_help_response(iph, len, ct, ctinfo, talk_port, mode,
++                                        ((struct ntalk_response *)data)->type, 
++                                        ((struct ntalk_response *)data)->answer,
++                                        &(((struct ntalk_response *)data)->addr));
++      else if (talk_port == NTALK_PORT
++               && ntalk2
++               && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_response)
++               && ((struct ntalk2_response *)data)->vers == NTALK2_VERSION)
++              return talk_help_response(iph, len, ct, ctinfo, talk_port, mode,
++                                        ((struct ntalk2_response *)data)->type, 
++                                        ((struct ntalk2_response *)data)->answer,
++                                        &(((struct ntalk2_response *)data)->addr));
++      else {
++              DEBUGP("ip_ct_talk_help: not ntalk/ntalk2 response, datalen %u != %u or %u + max 256\n", 
++                     (unsigned)udplen - sizeof(struct udphdr), 
++                     sizeof(struct ntalk_response), sizeof(struct ntalk2_response));
++              return NF_ACCEPT;
++      }
++}
++
++static int lookup_help(const struct iphdr *iph, size_t len,
++                     struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
++{
++      return talk_help(iph, len, ct, ctinfo, TALK_PORT, LOOK_UP);
++}
++
++static int lookup_nhelp(const struct iphdr *iph, size_t len,
++                      struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
++{
++      return talk_help(iph, len, ct, ctinfo, NTALK_PORT, LOOK_UP);
++}
++
++static struct ip_conntrack_helper lookup_helpers[2] = 
++      { { { NULL, NULL },
++          "talk",                                     /* name */
++          0,                                          /* flags */
++          NULL,                                       /* module */
++          1,                                          /* max_expected */
++          240,                                        /* timeout */
++            { { 0, { __constant_htons(TALK_PORT) } }, /* tuple */
++            { 0, { 0 }, IPPROTO_UDP } },
++          { { 0, { 0xFFFF } },                        /* mask */
++            { 0, { 0 }, 0xFFFF } },
++          lookup_help },                              /* helper */
++          { { NULL, NULL },
++            "ntalk",                                  /* name */
++          0,                                          /* flags */
++          NULL,                                       /* module */
++          1,                                          /* max_expected */
++          240,                                        /* timeout */
++          { { 0, { __constant_htons(NTALK_PORT) } },  /* tuple */
++            { 0, { 0 }, IPPROTO_UDP } },
++          { { 0, { 0xFFFF } },                        /* mask */
++            { 0, { 0 }, 0xFFFF } },
++          lookup_nhelp }                              /* helper */
++        };
++
++static int talk_expect(struct ip_conntrack *ct)
++{
++      DEBUGP("ip_conntrack_talk: calling talk_expectfn for ct %p\n", ct);
++      WRITE_LOCK(&ip_conntrack_lock);
++      ct->helper = &lookup_helpers[0];
++      WRITE_UNLOCK(&ip_conntrack_lock);
++       
++      return NF_ACCEPT;       /* unused */
++}
++
++static int ntalk_expect(struct ip_conntrack *ct)
++{
++      DEBUGP("ip_conntrack_talk: calling ntalk_expectfn for ct %p\n", ct);
++      WRITE_LOCK(&ip_conntrack_lock);
++      ct->helper = &lookup_helpers[1];
++      WRITE_UNLOCK(&ip_conntrack_lock);
++       
++      return NF_ACCEPT;       /* unused */
++}
++
++static int help(const struct iphdr *iph, size_t len,
++              struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
++{
++      return talk_help(iph, len, ct, ctinfo, TALK_PORT, ANNOUNCE);
++}
++
++static int nhelp(const struct iphdr *iph, size_t len,
++               struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
++{
++      return talk_help(iph, len, ct, ctinfo, NTALK_PORT, ANNOUNCE);
++}
++
++static struct ip_conntrack_helper talk_helpers[2] = 
++      { { { NULL, NULL },
++          "talk",                                     /* name */
++          0,                                          /* flags */
++          THIS_MODULE,                                /* module */
++          1,                                          /* max_expected */
++          240,                                        /* timeout */
++          { { 0, { __constant_htons(TALK_PORT) } },   /* tuple */
++            { 0, { 0 }, IPPROTO_UDP } },
++          { { 0, { 0xFFFF } },                        /* mask */
++            { 0, { 0 }, 0xFFFF } },
++          help },                                     /* helper */
++          { { NULL, NULL },
++          "ntalk",                                    /* name */
++          0,                                          /* flags */
++          THIS_MODULE,                                /* module */
++          1,                                          /* max_expected */
++          240,                                        /* timeout */
++          { { 0, { __constant_htons(NTALK_PORT) } },  /* tuple */
++            { 0, { 0 }, IPPROTO_UDP } },
++          { { 0, { 0xFFFF } },                        /* mask */
++            { 0, { 0 }, 0xFFFF } },
++          nhelp }                                     /* helper */
++      };
++
++static int __init init(void)
++{
++      if (talk > 0)
++              ip_conntrack_helper_register(&talk_helpers[0]);
++      if (ntalk > 0 || ntalk2 > 0)
++              ip_conntrack_helper_register(&talk_helpers[1]);
++              
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      if (talk > 0)
++              ip_conntrack_helper_unregister(&talk_helpers[0]);
++      if (ntalk > 0 || ntalk2 > 0)
++              ip_conntrack_helper_unregister(&talk_helpers[1]);
++}
++
++EXPORT_SYMBOL(ip_talk_lock);
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_tftp.c linux-2.4.20/net/ipv4/netfilter/ip_conntrack_tftp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_conntrack_tftp.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_conntrack_tftp.c        Wed Sep 24 09:16:24 2003
+@@ -0,0 +1,129 @@
++/*
++ * Licensed under GNU GPL version 2 Copyright Magnus Boden <mb@ozaba.mine.nu>
++ * Version: 0.0.7
++ *
++ * Thu 21 Mar 2002 Harald Welte <laforge@gnumonks.org>
++ *    - port to newnat API
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/udp.h>
++
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_tftp.h>
++
++MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>");
++MODULE_DESCRIPTION("Netfilter connection tracking module for tftp");
++MODULE_LICENSE("GPL");
++
++#define MAX_PORTS 8
++static int ports[MAX_PORTS];
++static int ports_c = 0;
++#ifdef MODULE_PARM
++MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
++MODULE_PARM_DESC(ports, "port numbers of tftp servers");
++#endif
++
++#if 0
++#define DEBUGP(format, args...) printk(__FILE__ ":" __FUNCTION__ ": " \
++                                     format, ## args)
++#else
++#define DEBUGP(format, args...)
++#endif
++
++static int tftp_help(const struct iphdr *iph, size_t len,
++      struct ip_conntrack *ct,
++      enum ip_conntrack_info ctinfo)
++{
++      struct udphdr *udph = (void *)iph + iph->ihl * 4;
++      struct tftphdr *tftph = (void *)udph + 8;
++      struct ip_conntrack_expect exp;
++      
++      switch (ntohs(tftph->opcode)) {
++      /* RRQ and WRQ works the same way */
++      case TFTP_OPCODE_READ:
++      case TFTP_OPCODE_WRITE:
++              DEBUGP("");
++              DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++              DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
++              memset(&exp, 0, sizeof(exp));
++
++              exp.tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
++              exp.mask.src.ip = 0xffffffff;
++              exp.mask.dst.ip = 0xffffffff;
++              exp.mask.dst.u.udp.port = 0xffff;
++              exp.mask.dst.protonum = 0xffff;
++              exp.expectfn = NULL;
++
++              DEBUGP("expect: ");
++              DUMP_TUPLE(&exp.tuple);
++              DUMP_TUPLE(&exp.mask);
++              ip_conntrack_expect_related(ct, &exp);
++              break;
++      default:
++              DEBUGP("Unknown opcode\n");
++      }
++      return NF_ACCEPT;
++}
++
++static struct ip_conntrack_helper tftp[MAX_PORTS];
++static char tftp_names[MAX_PORTS][10];
++
++static void fini(void)
++{
++      int i;
++
++      for (i = 0 ; i < ports_c; i++) {
++              DEBUGP("unregistering helper for port %d\n",
++                      ports[i]);
++              ip_conntrack_helper_unregister(&tftp[i]);
++      } 
++}
++
++static int __init init(void)
++{
++      int i, ret;
++      char *tmpname;
++
++      if (!ports[0])
++              ports[0]=TFTP_PORT;
++
++      for (i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) {
++              /* Create helper structure */
++              tftp[i].tuple.dst.protonum = IPPROTO_UDP;
++              tftp[i].tuple.src.u.udp.port = htons(ports[i]);
++              tftp[i].mask.dst.protonum = 0xFFFF;
++              tftp[i].mask.src.u.udp.port = 0xFFFF;
++              tftp[i].max_expected = 1;
++              tftp[i].timeout = 0;
++              tftp[i].flags = IP_CT_HELPER_F_REUSE_EXPECT;
++              tftp[i].me = THIS_MODULE;
++              tftp[i].help = tftp_help;
++
++              tmpname = &tftp_names[i][0];
++              if (ports[i] == TFTP_PORT)
++                      sprintf(tmpname, "tftp");
++              else
++                      sprintf(tmpname, "tftp-%d", i);
++              tftp[i].name = tmpname;
++
++              DEBUGP("port #%d: %d\n", i, ports[i]);
++
++              ret=ip_conntrack_helper_register(&tftp[i]);
++              if (ret) {
++                      printk("ERROR registering helper for port %d\n",
++                              ports[i]);
++                      fini();
++                      return(ret);
++              }
++              ports_c++;
++      }
++      return(0);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_amanda.c linux-2.4.20/net/ipv4/netfilter/ip_nat_amanda.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_amanda.c        Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_amanda.c    Wed Sep 24 09:16:24 2003
+@@ -0,0 +1,224 @@
++/* Amanda extension for TCP NAT alteration.
++ * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca>
++ * based on a copy of HW's ip_nat_irc.c as well as other modules
++ *
++ *      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_amanda.o
++ */
++
++#include <linux/module.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/ip.h>
++#include <linux/udp.h>
++#include <linux/kernel.h>
++#include <net/tcp.h>
++#include <net/udp.h>
++
++#include <linux/netfilter_ipv4/ip_nat.h>
++#include <linux/netfilter_ipv4/ip_nat_helper.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_amanda.h>
++
++
++#if 0
++#define DEBUGP printk
++#define DUMP_OFFSET(x)        printk("offset_before=%d, offset_after=%d, correction_pos=%u\n", x->offset_before, x->offset_after, x->correction_pos);
++#else
++#define DEBUGP(format, args...)
++#define DUMP_OFFSET(x)
++#endif
++
++MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>");
++MODULE_DESCRIPTION("Amanda network address translation module");
++MODULE_LICENSE("GPL");
++
++/* protects amanda part of conntracks */
++DECLARE_LOCK_EXTERN(ip_amanda_lock);
++
++static unsigned int
++amanda_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_amanda_expect *exp_info;
++      struct ip_conntrack *master = master_ct(ct);
++
++      IP_NF_ASSERT(info);
++      IP_NF_ASSERT(master);
++
++      IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
++
++      DEBUGP("nat_expected: We have a connection!\n");
++      exp_info = &ct->master->help.exp_amanda_info;
++
++      newdstip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
++      newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++      DEBUGP("nat_expected: %u.%u.%u.%u->%u.%u.%u.%u\n",
++             NIPQUAD(newsrcip), NIPQUAD(newdstip));
++
++      port = exp_info->port;
++
++      if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
++              newip = newsrcip;
++      else
++              newip = newdstip;
++
++      DEBUGP("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;
++
++      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)
++                              { .udp = { htons(port) } });
++      }
++
++      return ip_nat_setup_info(ct, &mr, hooknum);
++}
++
++static int amanda_data_fixup(struct ip_conntrack *ct,
++                        struct sk_buff **pskb,
++                        enum ip_conntrack_info ctinfo,
++                        struct ip_conntrack_expect *expect)
++{
++      u_int32_t newip;
++      /* DATA 99999 MESG 99999 INDEX 99999 */
++      char buffer[6];
++      struct ip_conntrack_expect *exp = expect;
++      struct ip_ct_amanda_expect *ct_amanda_info = &exp->help.exp_amanda_info;
++      struct ip_conntrack_tuple t = exp->tuple;
++      int port;
++
++      MUST_BE_LOCKED(&ip_amanda_lock);
++
++      newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
++      DEBUGP ("ip_nat_amanda_help: newip = %u.%u.%u.%u\n", NIPQUAD(newip));
++
++      /* Alter conntrack's expectations. */
++
++      /* We can read expect here without conntrack lock, since it's
++         only set in ip_conntrack_amanda, with ip_amanda_lock held
++         writable */
++
++      t.dst.ip = newip;
++      for (port = ct_amanda_info->port + 10; port != 0; port++) {
++              t.dst.u.tcp.port = htons(port);
++              if (ip_conntrack_change_expect(exp, &t) == 0)
++                      break;
++      }
++
++      if (port == 0)
++              return 0;
++
++      sprintf(buffer, "%u", port);
++
++      return ip_nat_mangle_udp_packet(pskb, ct, ctinfo, /* XXX exp->seq */ ct_amanda_info->offset, 
++                                      ct_amanda_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)
++{
++      int dir;
++
++      if (!exp)
++              DEBUGP("ip_nat_amanda: no exp!!");
++              
++      /* Only mangle things once: original direction in POST_ROUTING
++         and reply direction on PRE_ROUTING. */
++      dir = CTINFO2DIR(ctinfo);
++      if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
++            || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
++              DEBUGP("ip_nat_amanda_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"
++                     : hooknum == NF_IP_LOCAL_IN ? "INPUT" : "???");
++              return NF_ACCEPT;
++      }
++      DEBUGP("ip_nat_amanda_help: got beyond not touching: dir %s at hook %s for expect: ",
++                 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"
++                     : hooknum == NF_IP_LOCAL_IN ? "INPUT" : "???");
++      DUMP_TUPLE(&exp->tuple);
++
++      LOCK_BH(&ip_amanda_lock);
++// XXX        if (exp->seq != 0)
++      if (exp->help.exp_amanda_info.offset != 0)
++              /*  if this packet has a "seq" it needs to have it's content mangled */
++              if (!amanda_data_fixup(ct, pskb, ctinfo, exp)) {
++                      UNLOCK_BH(&ip_amanda_lock);
++                      DEBUGP("ip_nat_amanda: NF_DROP\n");
++                      return NF_DROP;
++              }
++      exp->help.exp_amanda_info.offset = 0;
++      UNLOCK_BH(&ip_amanda_lock);
++
++      DEBUGP("ip_nat_amanda: NF_ACCEPT\n");
++      return NF_ACCEPT;
++}
++
++static struct ip_nat_helper ip_nat_amanda_helper;
++
++/* This function is intentionally _NOT_ defined as  __exit, because
++ * it is needed by init() */
++static void fini(void)
++{
++      DEBUGP("ip_nat_amanda: unregistering nat helper\n");
++      ip_nat_helper_unregister(&ip_nat_amanda_helper);
++}
++
++static int __init init(void)
++{
++      int ret = 0;
++      struct ip_nat_helper *hlpr;
++
++      hlpr = &ip_nat_amanda_helper;
++      hlpr->tuple.dst.protonum = IPPROTO_UDP;
++      hlpr->tuple.src.u.udp.port = htons(10080);
++      hlpr->mask.src.u.udp.port = 0xFFFF;
++      hlpr->mask.dst.protonum = 0xFFFF;
++      hlpr->help = help;
++      hlpr->flags = 0;
++      hlpr->me = THIS_MODULE;
++      hlpr->expect = amanda_nat_expected;
++
++      hlpr->name = "amanda";
++
++      DEBUGP
++          ("ip_nat_amanda: Trying to register nat helper\n");
++      ret = ip_nat_helper_register(hlpr);
++
++      if (ret) {
++              printk
++                  ("ip_nat_amanda: error registering nat helper\n");
++              fini();
++              return 1;
++      }
++      return ret;
++}
++
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_core.c linux-2.4.20/net/ipv4/netfilter/ip_nat_core.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_core.c  Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_core.c      Wed Sep 24 09:18:12 2003
+@@ -1,10 +1,9 @@
+ /* NAT for netfilter; shared with compatibility layer. */
+ /* (c) 1999 Paul `Rusty' Russell.  Licenced under the GNU General
+-   Public Licence. */
+-#ifdef MODULE
+-#define __NO_VERSION__
+-#endif
++ * Public Licence. 
++ * (c) 2000-2002 by the netfilter core team <coreteam@netfilter.org>
++ */
+ #include <linux/version.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+@@ -70,6 +69,7 @@
+ static void ip_nat_cleanup_conntrack(struct ip_conntrack *conn)
+ {
+       struct ip_nat_info *info = &conn->nat.info;
++      unsigned int hs, hp;
+       if (!info->initialized)
+               return;
+@@ -77,27 +77,31 @@
+       IP_NF_ASSERT(info->bysource.conntrack);
+       IP_NF_ASSERT(info->byipsproto.conntrack);
++      hs = hash_by_src(&conn->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src,
++                       conn->tuplehash[IP_CT_DIR_ORIGINAL]
++                       .tuple.dst.protonum);
++
++      hp = hash_by_ipsproto(conn->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip,
++                            conn->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip,
++                            conn->tuplehash[IP_CT_DIR_REPLY]
++                            .tuple.dst.protonum);
++
+       WRITE_LOCK(&ip_nat_lock);
+-      LIST_DELETE(&bysource[hash_by_src(&conn->tuplehash[IP_CT_DIR_ORIGINAL]
+-                                        .tuple.src,
+-                                        conn->tuplehash[IP_CT_DIR_ORIGINAL]
+-                                        .tuple.dst.protonum)],
+-                  &info->bysource);
+-
+-      LIST_DELETE(&byipsproto
+-                  [hash_by_ipsproto(conn->tuplehash[IP_CT_DIR_REPLY]
+-                                    .tuple.src.ip,
+-                                    conn->tuplehash[IP_CT_DIR_REPLY]
+-                                    .tuple.dst.ip,
+-                                    conn->tuplehash[IP_CT_DIR_REPLY]
+-                                    .tuple.dst.protonum)],
+-                  &info->byipsproto);
++      LIST_DELETE(&bysource[hs], &info->bysource);
++      LIST_DELETE(&byipsproto[hp], &info->byipsproto);
+       WRITE_UNLOCK(&ip_nat_lock);
+ }
+-/* We do checksum mangling, so if they were wrong before they're still
+- * wrong.  Also works for incomplete packets (eg. ICMP dest
+- * unreachables.) */
++/**
++ * ip_nat_cheat_check - Incremental checksum change for IP/TCP checksum
++ * @oldvalinv: bit-inverted old value of 32bit word
++ * @newval: new value of 32bit word
++ * @oldcheck: old checksum value
++ *
++ * This function implements incremental checksum mangling, so if a checksum
++ * was wrong it will still be wrong after mangling.  Also works for incomplete
++ * packets (eg. ICMP dest unreachables).  Return value is the new checksum.
++ */
+ u_int16_t
+ ip_nat_cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck)
+ {
+@@ -123,7 +127,14 @@
+       return i;
+ }
+-/* Is this tuple already taken? (not by us) */
++/**
++ * ip_nat_used_tuple - Is this tuple already in use?
++ * @tuple: tuple to be used for this check
++ * @ignored_conntrack: conntrack excluded from this check
++ *
++ * This function checks for the reply (inverted) tuple in the conntrack
++ * hash.  This is necessarry with NAT, since there is no fixed mapping.
++ */
+ int
+ ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple,
+                 const struct ip_conntrack *ignored_conntrack)
+@@ -162,8 +173,8 @@
+                               continue;
+               }
+-              if ((mr->range[i].flags & IP_NAT_RANGE_PROTO_SPECIFIED)
+-                  && proto->in_range(&newtuple, IP_NAT_MANIP_SRC,
++              if (!(mr->range[i].flags & IP_NAT_RANGE_PROTO_SPECIFIED)
++                  || proto->in_range(&newtuple, IP_NAT_MANIP_SRC,
+                                      &mr->range[i].min, &mr->range[i].max))
+                       return 1;
+       }
+@@ -247,11 +258,12 @@
+          const struct ip_conntrack *conntrack)
+ {
+       unsigned int score = 0;
++      unsigned int h;
+       MUST_BE_READ_LOCKED(&ip_nat_lock);
+-      LIST_FIND(&byipsproto[hash_by_ipsproto(src, dst, protonum)],
+-                fake_cmp, struct ip_nat_hash *, src, dst, protonum, &score,
+-                conntrack);
++      h = hash_by_ipsproto(src, dst, protonum);
++      LIST_FIND(&byipsproto[h], fake_cmp, struct ip_nat_hash *,
++                src, dst, protonum, &score, conntrack);
+       return score;
+ }
+@@ -434,7 +446,7 @@
+       *tuple = *orig_tuple;
+       while ((rptr = find_best_ips_proto_fast(tuple, mr, conntrack, hooknum))
+              != NULL) {
+-              DEBUGP("Found best for "); DUMP_TUPLE(tuple);
++              DEBUGP("Found best for "); DUMP_TUPLE_RAW(tuple);
+               /* 3) The per-protocol part of the manip is made to
+                  map into the range to make a unique tuple. */
+@@ -511,6 +523,19 @@
+ #endif
+ };
++/**
++ * ip_nat_setup_info - Set up NAT mappings for NEW packet
++ * @conntrack: conntrack on which we operate
++ * @mr: address/port range which is valid for this NAT mapping
++ * @hooknum: hook at which this NAT mapping applies
++ *
++ * This function is called by NAT targets (SNAT,DNAT,...) and by
++ * the NAT application helper modules.  It is called for the NEW packet
++ * of a connection in order to specify which NAT mappings shall apply to
++ * this connection at a given hook.
++ *
++ * Note: The reply mappings are created automagically by this function. 
++ */
+ unsigned int
+ ip_nat_setup_info(struct ip_conntrack *conntrack,
+                 const struct ip_nat_multi_range *mr,
+@@ -519,12 +544,14 @@
+       struct ip_conntrack_tuple new_tuple, inv_tuple, reply;
+       struct ip_conntrack_tuple orig_tp;
+       struct ip_nat_info *info = &conntrack->nat.info;
++      int in_hashes = info->initialized;
+       MUST_BE_WRITE_LOCKED(&ip_nat_lock);
+       IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
+                    || hooknum == NF_IP_POST_ROUTING
+                    || hooknum == NF_IP_LOCAL_OUT);
+       IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS);
++      IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
+       /* What we've got will look like inverse of reply. Normally
+          this is what is in the conntrack, except for prior
+@@ -573,9 +600,9 @@
+                      HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST",
+                      conntrack);
+               DEBUGP("Original: ");
+-              DUMP_TUPLE(&orig_tp);
++              DUMP_TUPLE_RAW(&orig_tp);
+               DEBUGP("New: ");
+-              DUMP_TUPLE(&new_tuple);
++              DUMP_TUPLE_RAW(&new_tuple);
+ #endif
+               /* We now have two tuples (SRCIP/SRCPT/DSTIP/DSTPT):
+@@ -641,6 +668,14 @@
+       /* It's done. */
+       info->initialized |= (1 << HOOK2MANIP(hooknum));
++
++      if (in_hashes) {
++              IP_NF_ASSERT(info->bysource.conntrack);
++              replace_in_hashes(conntrack, info);
++      } else {
++              place_in_hashes(conntrack, info);
++      }
++
+       return NF_ACCEPT;
+ }
+@@ -799,6 +834,7 @@
+               struct ip_conntrack_expect *exp = NULL;
+               struct list_head *cur_item;
+               int ret = NF_ACCEPT;
++              int helper_called = 0;
+               DEBUGP("do_bindings: helper existing for (%p)\n", ct);
+@@ -817,19 +853,21 @@
+                               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);
++                              /* 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_called = 1;
+                       }
+               }
+-              /* Helper might want to manip the packet even when there is no expectation */
+-              if (!exp && helper->flags & IP_NAT_HELPER_F_ALWAYS) {
++              /* Helper might want to manip the packet even when there is no
++               * matching expectation for this packet */
++              if (!helper_called && helper->flags & IP_NAT_HELPER_F_ALWAYS) {
+                       DEBUGP("calling nat helper for packet without expectation\n");
+                       ret = helper->help(ct, NULL, info, ctinfo, 
+                                          hooknum, pskb);
+@@ -915,7 +953,7 @@
+                  packet, except it was never src/dst reversed, so
+                  where we would normally apply a dst manip, we apply
+                  a src, and vice versa. */
+-              if (info->manips[i].hooknum == opposite_hook[hooknum]) {
++              if (info->manips[i].hooknum == hooknum) {
+                       DEBUGP("icmp_reply: inner %s -> %u.%u.%u.%u %u\n",
+                              info->manips[i].maniptype == IP_NAT_MANIP_SRC
+                              ? "DST" : "SRC",
+@@ -926,9 +964,9 @@
+                                 &info->manips[i].manip,
+                                 !info->manips[i].maniptype,
+                                 &skb->nfcache);
+-              /* Outer packet needs to have IP header NATed like
+-                   it's a reply. */
+-              } else if (info->manips[i].hooknum == hooknum) {
++                      /* Outer packet needs to have IP header NATed like
++                         it's a reply. */
++
+                       /* Use mapping to map outer packet: 0 give no
+                            per-proto mapping */
+                       DEBUGP("icmp_reply: outer %s -> %u.%u.%u.%u\n",
+@@ -982,6 +1020,10 @@
+       /* FIXME: Man, this is a hack.  <SIGH> */
+       IP_NF_ASSERT(ip_conntrack_destroyed == NULL);
+       ip_conntrack_destroyed = &ip_nat_cleanup_conntrack;
++      
++      /* Initialize fake conntrack so that NAT will skip it */
++      ip_conntrack_untracked.nat.info.initialized |= 
++              (1 << IP_NAT_MANIP_SRC) | (1 << IP_NAT_MANIP_DST);
+       return 0;
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_cuseeme.c linux-2.4.20/net/ipv4/netfilter/ip_nat_cuseeme.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_cuseeme.c       Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_cuseeme.c   Wed Sep 24 09:17:38 2003
+@@ -0,0 +1,289 @@
++/* CuSeeMe extension for UDP NAT alteration.
++ * (C) 2002 by Filip Sneppe <filip.sneppe@cronos.be>
++ * based on ip_masq_cuseeme.c in 2.2 kernels
++ *
++ * ip_nat_cuseeme.c v0.0.7 2003-02-18
++ *
++ *      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_cuseeme.o ports=port1,port2,...port<MAX_PORTS>
++ *
++ *      Please give the ports of the CuSeeMe traffic you want to track.
++ *      If you don't specify ports, the default will be UDP port 7648.
++ *
++ *      CuSeeMe Protocol Documentation:
++ *      http://cu-seeme.net/squeek/tech/contents.html
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/ip.h>
++#include <linux/udp.h>
++
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_cuseeme.h>
++#include <linux/netfilter_ipv4/ip_nat_helper.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++
++MODULE_AUTHOR("Filip Sneppe <filip.sneppe@cronos.be>");
++MODULE_DESCRIPTION("Netfilter NAT helper for CuSeeMe");
++MODULE_LICENSE("GPL");
++
++#define MAX_PORTS 8
++
++static int ports[MAX_PORTS];
++static int ports_c = 0;
++#ifdef MODULE_PARM
++MODULE_PARM(ports,"1-" __MODULE_STRING(MAX_PORTS) "i");
++MODULE_PARM_DESC(ports, "port numbers of CuSeeMe reflectors");
++#endif
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++/* process packet from client->reflector, possibly manipulate client IP in payload */
++void cuseeme_mangle_outgoing(struct ip_conntrack *ct,
++                             struct ip_nat_info *info,
++                             enum ip_conntrack_info ctinfo,
++                             struct sk_buff **pskb,
++                             char *data,
++                             unsigned int datalen)
++{
++      char new_port_ip[6];
++      struct cu_header *cu_head=(struct cu_header *)data;
++      
++      DEBUGP("ip_nat_cuseeme: outgoing packet, ID %u, dest_family %u\n", 
++             ntohs(cu_head->data_type), ntohs(cu_head->dest_family));
++              
++      /* At least check that the data at offset 10 is the client's port and IP address */
++      if ((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip == cu_head->addr) &&
++          (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port == cu_head->port)) {
++              DEBUGP("ip_nat_cuseeme: rewrite outgoing client %u.%u.%u.%u:%u->%u.%u.%u.%u:%u at offset 10\n", 
++                     NIPQUAD(cu_head->addr),
++                     ntohs(cu_head->port),
++                     NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip),
++                     ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port));
++              *((u_int16_t *)new_port_ip) = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port;
++              *((u_int32_t *)(new_port_ip+2)) = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++              /* at offset 10, replace 6 bytes containing port + IP address */
++              ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
++                                       10, 6, (char *)(new_port_ip), 6);
++      } else 
++              DEBUGP("ip_nat_cuseeme: expected outgoing client %u.%u.%u.%u:%u, but got %u.%u.%u.%u:%u\n",
++                     NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
++                     ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port),
++                     NIPQUAD(cu_head->addr),
++                     ntohs(cu_head->port));
++}
++
++/* process packet from reflector->client, possibly manipulate client IP & reflector IP in payload */
++void cuseeme_mangle_incoming(struct ip_conntrack *ct,
++                             struct ip_nat_info *info,
++                             enum ip_conntrack_info ctinfo,
++                             struct sk_buff **pskb,
++                             char *data,
++                             unsigned int datalen)
++{
++      char new_port_ip[6];
++      struct cu_header *cu_head = (struct cu_header *)data;
++      struct oc_header *oc_head = (struct oc_header *)data; 
++      struct client_info *ci; 
++      int i, off;
++
++      
++      DEBUGP("ip_nat_cuseeme: incoming packet, ID %u, dest_family %u\n", 
++             ntohs(cu_head->data_type), ntohs(cu_head->dest_family));
++              
++      /* Check if we're really dealing with the client's port + IP address before rewriting */
++      if((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip == cu_head->dest_addr) &&
++         (ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port == cu_head->dest_port)) {
++              DEBUGP("ip_nat_cuseeme: rewrite incoming client %u.%u.%u.%u:%u->%u.%u.%u.%u:%u at offset 2\n",
++                     NIPQUAD(cu_head->dest_addr),
++                     ntohs(cu_head->dest_port),
++                     NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
++                     ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port));
++              *((u_int16_t *)new_port_ip) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
++              *((u_int32_t *)(new_port_ip+2)) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++              /* at offset 2, replace 6 bytes containing port + IP address */
++              ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
++                                       2, 6, (char *)(new_port_ip), 6);
++      } else 
++              DEBUGP("ip_nat_cuseeme: expected incoming client %u.%u.%u.%u:%u, but got %u.%u.%u.%u:%u\n",
++                     NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip),
++                     ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port),
++                     NIPQUAD(cu_head->dest_addr),
++                     ntohs(cu_head->dest_port));
++      
++      /* Check if we're really dealing with the server's port + IP address before rewriting. 
++         In some cases, the IP address == 0.0.0.0 so we don't rewrite anything */
++      if((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip == cu_head->addr) &&
++         (ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port == cu_head->port)) {
++              DEBUGP("in_nat_cuseeme: rewrite incoming server %u.%u.%u.%u:%u->%u.%u.%u.%u:%u at offset 10\n",
++                     NIPQUAD(cu_head->addr),
++                     ntohs(cu_head->port),
++                     NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip),
++                     ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port));
++              *((u_int16_t *)new_port_ip) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port;
++              *((u_int32_t *)(new_port_ip+2)) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
++              /* at offset 10, replace 6 bytes containing port + IP address */
++              ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
++                                       10, 6, (char *)(new_port_ip), 6);
++      } else 
++              /* Sometimes we find 0.0.0.0, sometimes an IP address - the docs say this field
++                 is not that important so we're not logging this unless we're debugging */
++              DEBUGP("ip_nat_cuseeme: no biggie, expected incoming server %u.%u.%u.%u:%u, but got %u.%u.%u.%u:%u\n",
++                     NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip),
++                     ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port),
++                     NIPQUAD(cu_head->addr),
++                     ntohs(cu_head->port));
++      
++      /* Spin through client_info structs until we find our own */
++      if((ntohs(cu_head->data_type) == 101) && (datalen >= sizeof(struct oc_header))) {
++              DEBUGP("ip_nat_cuseeme: looping through %u client_info structs\n", oc_head->client_count);
++              for(i=0, off=sizeof(struct oc_header);
++                  (i < oc_head->client_count && 
++                  off+sizeof(struct client_info) <= datalen); 
++                  i++) {
++                      ci=(struct client_info *)(data+off);
++                      DEBUGP("ip_nat_cuseeme: comparing %u.%u.%u.%u with %u.%u.%u.%u at offset %u\n", 
++                             NIPQUAD(ci->address), NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), 
++                             (unsigned int)((void *)&(ci->address) - (void *)cu_head));
++                      if(ci->address == ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip) {
++                              /* mangle this IP address */
++                              DEBUGP("ip_nat_cuseeme: changing %u.%u.%u.%u->%u.%u.%u.%u at offset %u\n",
++                                     NIPQUAD(ci->address), 
++                                     NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
++                                     (unsigned int)((void *)&(ci->address) - (void *)cu_head));
++                              ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
++                                                       (unsigned int)((void *)&(ci->address) - (void *)cu_head), 4, 
++                                                       (char *)(&(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip)), 4);
++                              break;
++                      } else 
++                              off+=sizeof(struct client_info);
++              }
++      } else
++              DEBUGP("ip_nat_cuseeme: data_type %u, datalen %u < sizeof(struct oc_header) %u\n", 
++                     ntohs(cu_head->data_type), datalen, sizeof(struct oc_header));
++}
++
++static unsigned int 
++cuseeme_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)
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct udphdr *udph = (void *)iph + iph->ihl * 4;
++      int dir = CTINFO2DIR(ctinfo);
++      unsigned int datalen = (*pskb)->len - iph->ihl * 4 - sizeof(struct udphdr);
++      char *data = (char *) &udph[1];
++      
++      DEBUGP("ip_nat_cuseeme: cuseeme_nat_help, direction: %s 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" : "???"
++            );
++      
++      /* Only mangle things once: original direction in POST_ROUTING
++         and reply direction on PRE_ROUTING. */
++      if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
++          || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
++              DEBUGP("ip_nat_cuseeme: 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(datalen < sizeof(struct cu_header)) {
++              /* packet too small */
++              if (net_ratelimit())
++                      printk("ip_nat_cuseeme: payload too small (%u, should be >= %u)\n", 
++                             datalen, sizeof(struct cu_header));
++              return NF_ACCEPT;
++      }
++
++
++      /* In the debugging output, "outgoing" is from client to server, and
++         "incoming" is from server to client */
++      if(HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) 
++              cuseeme_mangle_outgoing(ct, info, ctinfo, pskb, data, datalen);
++      else 
++              cuseeme_mangle_incoming(ct, info, ctinfo, pskb, data, datalen);
++
++      return NF_ACCEPT;
++}
++
++static struct ip_nat_helper cuseeme[MAX_PORTS];
++static char cuseeme_names[MAX_PORTS][14];  /* cuseeme-65535 */
++
++static void fini(void)
++{
++      int i;
++      
++      for (i = 0 ; i < ports_c; i++) {
++              DEBUGP("ip_nat_cuseeme: unregistering helper for port %d\n", ports[i]);
++                     ip_nat_helper_unregister(&cuseeme[i]);
++      }
++}
++
++static int __init init(void)
++{
++      int i, ret = 0;
++      char *tmpname;
++
++      if (!ports[0])
++              ports[0] = CUSEEME_PORT;
++              
++      for (i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) {
++              memset(&cuseeme[i], 0, sizeof(struct ip_nat_helper));
++
++              cuseeme[i].tuple.dst.protonum = IPPROTO_UDP;
++              cuseeme[i].tuple.dst.u.udp.port = htons(ports[i]);
++              cuseeme[i].mask.dst.protonum = 0xFFFF;
++              cuseeme[i].mask.dst.u.udp.port = 0xFFFF;
++              cuseeme[i].help = cuseeme_nat_help;
++              cuseeme[i].flags = IP_NAT_HELPER_F_STANDALONE + 
++                                 IP_NAT_HELPER_F_ALWAYS; /* dunno if IP_NAT_HELPER_F_ALWAYS
++                                                            is stricly needed... */
++              cuseeme[i].me = THIS_MODULE;
++              cuseeme[i].expect = NULL; /* cuseeme_nat_expected; */
++                      
++              tmpname = &cuseeme_names[i][0];
++              if (ports[i] == CUSEEME_PORT)
++                      sprintf(tmpname, "cuseeme");
++              else
++                      sprintf(tmpname, "cuseeme-%d", ports[i]);
++              cuseeme[i].name = tmpname;
++                      
++              DEBUGP("ip_nat_cuseeme: registering helper for port %d: name %s\n",
++                     ports[i], cuseeme[i].name);
++              ret = ip_nat_helper_register(&cuseeme[i]);
++                      
++              if (ret) {
++                      printk("ip_nat_cuseeme: unable to register helper for port %d\n",
++                             ports[i]);
++                      fini();
++                      return ret;
++              }
++              ports_c++;
++      }
++      return ret;
++}
++      
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_ftp.c linux-2.4.20/net/ipv4/netfilter/ip_nat_ftp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_ftp.c   Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_ftp.c       Wed Sep 24 09:16:17 2003
+@@ -84,7 +84,7 @@
+               mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+               mr.range[0].min = mr.range[0].max
+                       = ((union ip_conntrack_manip_proto)
+-                              { htons(exp_ftp_info->port) });
++                              { .tcp = { htons(exp_ftp_info->port) } });
+       }
+       return ip_nat_setup_info(ct, &mr, hooknum);
+ }
+@@ -306,9 +306,6 @@
+               ports[0] = FTP_PORT;
+       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;
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_h323.c linux-2.4.20/net/ipv4/netfilter/ip_nat_h323.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_h323.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_h323.c      Wed Sep 24 09:17:43 2003
+@@ -0,0 +1,419 @@
++/* 
++ * H.323 'brute force' extension for NAT alteration. 
++ * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
++ *
++ * 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 <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++#include <net/tcp.h>
++
++#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/netfilter_ipv4/ip_nat.h>
++#include <linux/netfilter_ipv4/ip_nat_helper.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
++
++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
++MODULE_DESCRIPTION("H.323 'brute force' connection tracking module");
++MODULE_LICENSE("GPL");
++
++DECLARE_LOCK_EXTERN(ip_h323_lock);
++struct module *ip_nat_h323 = THIS_MODULE;
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++/* FIXME: Time out? --RR */
++
++static unsigned int 
++h225_nat_expected(struct sk_buff **pskb,
++                unsigned int hooknum,
++                struct ip_conntrack *ct,
++                struct ip_nat_info *info);
++
++static unsigned int h225_nat_help(struct ip_conntrack *ct,
++                                struct ip_conntrack_expect *exp,
++                                struct ip_nat_info *info,
++                                enum ip_conntrack_info ctinfo,
++                                unsigned int hooknum,
++                                struct sk_buff **pskb);
++                
++static struct ip_nat_helper h245 = 
++      { { NULL, NULL },
++          "H.245",                            /* name */
++        0,                                    /* flags */
++        NULL,                                 /* module */
++        { { 0, { 0 } },                       /* tuple */
++          { 0, { 0 }, IPPROTO_TCP } },
++        { { 0, { 0xFFFF } },                  /* mask */
++          { 0, { 0 }, 0xFFFF } },
++        h225_nat_help,                        /* helper */
++        h225_nat_expected                     /* expectfn */
++      };
++
++static unsigned int
++h225_nat_expected(struct sk_buff **pskb,
++                unsigned int hooknum,
++                struct ip_conntrack *ct,
++                struct ip_nat_info *info)
++{
++      struct ip_nat_multi_range mr;
++      u_int32_t newdstip, newsrcip, newip;
++      u_int16_t port;
++      struct ip_ct_h225_expect *exp_info;
++      struct ip_ct_h225_master *master_info;
++      struct ip_conntrack *master = master_ct(ct);
++      unsigned int is_h225, ret;
++      
++      IP_NF_ASSERT(info);
++      IP_NF_ASSERT(master);
++
++      IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
++
++      DEBUGP("h225_nat_expected: We have a connection!\n");
++      master_info = &ct->master->expectant->help.ct_h225_info;
++      exp_info = &ct->master->help.exp_h225_info;
++
++      LOCK_BH(&ip_h323_lock);
++
++      DEBUGP("master: ");
++      DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++      DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_REPLY].tuple);
++      DEBUGP("conntrack: ");
++      DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++      if (exp_info->dir == IP_CT_DIR_ORIGINAL) {
++              /* Make connection go to the client. */
++              newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++              newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
++              DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to client)\n",
++                     NIPQUAD(newsrcip), NIPQUAD(newdstip));
++      } else {
++              /* Make the connection go to the server */
++              newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
++              newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++              DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to server)\n",
++                     NIPQUAD(newsrcip), NIPQUAD(newdstip));
++      }
++      port = exp_info->port;
++      is_h225 = master_info->is_h225 == H225_PORT;
++      UNLOCK_BH(&ip_h323_lock);
++      
++      if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
++              newip = newsrcip;
++      else
++              newip = newdstip;
++
++      DEBUGP("h225_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
++
++      mr.rangesize = 1;
++      /* We don't want to manip the per-protocol, just the IPs... */
++      mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
++      mr.range[0].min_ip = mr.range[0].max_ip = newip;
++
++      /* ... unless we're doing a MANIP_DST, in which case, make
++         sure we map to the correct port */
++      if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
++              mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
++              mr.range[0].min = mr.range[0].max
++                      = ((union ip_conntrack_manip_proto)
++                              { .tcp = { port } });
++      }
++
++      ret = ip_nat_setup_info(ct, &mr, hooknum);
++      
++      if (is_h225) {
++              DEBUGP("h225_nat_expected: H.225, setting NAT helper for %p\n", ct);
++              /* NAT expectfn called with ip_nat_lock write-locked */
++              info->helper = &h245;
++      }
++      return ret;
++}
++
++static int h323_signal_address_fixup(struct ip_conntrack *ct,
++                                   struct sk_buff **pskb,
++                                   enum ip_conntrack_info ctinfo)
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct tcphdr *tcph = (void *)iph + iph->ihl*4;
++      char *data = (char *) tcph + tcph->doff * 4;
++      u_int32_t tcplen = (*pskb)->len - iph->ihl*4;
++      u_int32_t datalen = tcplen - tcph->doff*4;
++      struct ip_ct_h225_master *info = &ct->help.ct_h225_info; 
++      u_int32_t newip;
++      u_int16_t port;
++      int i;
++
++      MUST_BE_LOCKED(&ip_h323_lock);
++
++      DEBUGP("h323_signal_address_fixup: %s %s\n",
++              between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
++                      ? "yes" : "no",
++              between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
++                      ? "yes" : "no");
++      if (!(between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
++              || between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)))
++              return 1;
++
++      DEBUGP("h323_signal_address_fixup: offsets %u + 6  and %u + 6 in %u\n", 
++              info->offset[IP_CT_DIR_ORIGINAL], 
++              info->offset[IP_CT_DIR_REPLY],
++              tcplen);
++      DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++      DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
++
++      for (i = 0; i < IP_CT_DIR_MAX; i++) {
++              DEBUGP("h323_signal_address_fixup: %s %s\n",
++                      info->dir == IP_CT_DIR_ORIGINAL ? "original" : "reply",
++                      i == IP_CT_DIR_ORIGINAL ? "caller" : "callee");
++              if (!between(info->seq[i], ntohl(tcph->seq), 
++                           ntohl(tcph->seq) + datalen))
++                      continue;
++              if (!between(info->seq[i] + 6, ntohl(tcph->seq),
++                           ntohl(tcph->seq) + datalen)) {
++                      /* Partial retransmisison. It's a cracker being funky. */
++                      if (net_ratelimit()) {
++                              printk("H.323_NAT: partial packet %u/6 in %u/%u\n",
++                                   info->seq[i],
++                                   ntohl(tcph->seq),
++                                   ntohl(tcph->seq) + datalen);
++                      }
++                      return 0;
++              }
++
++              /* Change address inside packet to match way we're mapping
++                 this connection. */
++              if (i == IP_CT_DIR_ORIGINAL) {
++                      newip = ct->tuplehash[!info->dir].tuple.dst.ip;
++                      port = ct->tuplehash[!info->dir].tuple.dst.u.tcp.port;
++              } else {
++                      newip = ct->tuplehash[!info->dir].tuple.src.ip;
++                      port = ct->tuplehash[!info->dir].tuple.src.u.tcp.port;
++              }
++
++              DEBUGP("h323_signal_address_fixup: orig %s IP:port %u.%u.%u.%u:%u\n", 
++                      i == IP_CT_DIR_ORIGINAL ? "source" : "dest  ", 
++                      NIPQUAD(*((u_int32_t *)(data + info->offset[i]))), 
++                      ntohs(*((u_int16_t *)(data + info->offset[i] + 4))));
++
++              /* Modify the packet */
++              *(u_int32_t *)(data + info->offset[i]) = newip;
++              *(u_int16_t *)(data + info->offset[i] + 4) = port;
++      
++              DEBUGP("h323_signal_address_fixup:  new %s IP:port %u.%u.%u.%u:%u\n", 
++                      i == IP_CT_DIR_ORIGINAL ? "source" : "dest  ", 
++                      NIPQUAD(*((u_int32_t *)(data + info->offset[i]))), 
++                      ntohs(*((u_int16_t *)(data + info->offset[i] + 4))));
++      }
++
++      /* fix checksum information */
++
++      (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4,
++                                   datalen, 0);
++
++      tcph->check = 0;
++      tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
++                                 csum_partial((char *)tcph, tcph->doff*4,
++                                         (*pskb)->csum));
++      ip_send_check(iph);
++
++      return 1;
++}
++
++static int h323_data_fixup(struct ip_ct_h225_expect *info,
++                         struct ip_conntrack *ct,
++                         struct sk_buff **pskb,
++                         enum ip_conntrack_info ctinfo,
++                         struct ip_conntrack_expect *expect)
++{
++      u_int32_t newip;
++      u_int16_t port;
++      struct ip_conntrack_tuple newtuple;
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct tcphdr *tcph = (void *)iph + iph->ihl*4;
++      char *data = (char *) tcph + tcph->doff * 4;
++      u_int32_t tcplen = (*pskb)->len - iph->ihl*4;
++      struct ip_ct_h225_master *master_info = &ct->help.ct_h225_info;
++      int is_h225;
++
++      MUST_BE_LOCKED(&ip_h323_lock);
++      DEBUGP("h323_data_fixup: offset %u + 6 in %u\n", info->offset, tcplen);
++      DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++      DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
++
++      if (!between(expect->seq + 6, ntohl(tcph->seq),
++                  ntohl(tcph->seq) + tcplen - tcph->doff * 4)) {
++              /* Partial retransmisison. It's a cracker being funky. */
++              if (net_ratelimit()) {
++                      printk("H.323_NAT: partial packet %u/6 in %u/%u\n",
++                           expect->seq,
++                           ntohl(tcph->seq),
++                           ntohl(tcph->seq) + tcplen - tcph->doff * 4);
++              }
++              return 0;
++      }
++
++      /* Change address inside packet to match way we're mapping
++         this connection. */
++      if (info->dir == IP_CT_DIR_REPLY) {
++              /* Must be where client thinks server is */
++              newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
++              /* Expect something from client->server */
++              newtuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++              newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
++      } else {
++              /* Must be where server thinks client is */
++              newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++              /* Expect something from server->client */
++              newtuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
++              newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++      }
++
++      is_h225 = (master_info->is_h225 == H225_PORT);
++
++      if (is_h225) {
++              newtuple.dst.protonum = IPPROTO_TCP;
++              newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
++      } else {
++              newtuple.dst.protonum = IPPROTO_UDP;
++              newtuple.src.u.udp.port = expect->tuple.src.u.udp.port;
++      }
++      
++      /* Try to get same port: if not, try to change it. */
++      for (port = ntohs(info->port); port != 0; port++) {
++              if (is_h225)
++                      newtuple.dst.u.tcp.port = htons(port);
++              else
++                      newtuple.dst.u.udp.port = htons(port);
++
++              if (ip_conntrack_change_expect(expect, &newtuple) == 0)
++                      break;
++      }
++      if (port == 0) {
++              DEBUGP("h323_data_fixup: no free port found!\n");
++              return 0;
++      }
++
++      port = htons(port);
++
++      DEBUGP("h323_data_fixup: orig IP:port %u.%u.%u.%u:%u\n", 
++              NIPQUAD(*((u_int32_t *)(data + info->offset))), 
++              ntohs(*((u_int16_t *)(data + info->offset + 4))));
++
++      /* Modify the packet */
++      *(u_int32_t *)(data + info->offset) = newip;
++      *(u_int16_t *)(data + info->offset + 4) = port;
++      
++      DEBUGP("h323_data_fixup: new IP:port %u.%u.%u.%u:%u\n", 
++              NIPQUAD(*((u_int32_t *)(data + info->offset))), 
++              ntohs(*((u_int16_t *)(data + info->offset + 4))));
++
++      /* fix checksum information  */
++      /* FIXME: usually repeated multiple times in the case of H.245! */
++
++      (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4,
++                                   tcplen - tcph->doff*4, 0);
++
++      tcph->check = 0;
++      tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
++                                 csum_partial((char *)tcph, tcph->doff*4,
++                                         (*pskb)->csum));
++      ip_send_check(iph);
++
++      return 1;
++}
++
++static unsigned int h225_nat_help(struct ip_conntrack *ct,
++                                struct ip_conntrack_expect *exp,
++                                struct ip_nat_info *info,
++                                enum ip_conntrack_info ctinfo,
++                                unsigned int hooknum,
++                                struct sk_buff **pskb)
++{
++      int dir;
++      struct ip_ct_h225_expect *exp_info;
++      
++      /* Only mangle things once: original direction in POST_ROUTING
++         and reply direction on PRE_ROUTING. */
++      dir = CTINFO2DIR(ctinfo);
++      DEBUGP("nat_h323: dir %s at hook %s\n",
++             dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
++             hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
++             : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
++             : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
++      if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
++            || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
++              DEBUGP("nat_h323: Not touching dir %s at hook %s\n",
++                     dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
++                     hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
++                     : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
++                     : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
++              return NF_ACCEPT;
++      }
++
++      if (!exp) {
++              LOCK_BH(&ip_h323_lock);
++              if (!h323_signal_address_fixup(ct, pskb, ctinfo)) {
++                      UNLOCK_BH(&ip_h323_lock);
++                      return NF_DROP;
++              }
++              UNLOCK_BH(&ip_h323_lock);
++              return NF_ACCEPT;
++      }
++              
++      exp_info = &exp->help.exp_h225_info;
++
++      LOCK_BH(&ip_h323_lock);
++      if (!h323_data_fixup(exp_info, ct, pskb, ctinfo, exp)) {
++              UNLOCK_BH(&ip_h323_lock);
++              return NF_DROP;
++      }
++      UNLOCK_BH(&ip_h323_lock);
++
++      return NF_ACCEPT;
++}
++
++static struct ip_nat_helper h225 = 
++      { { NULL, NULL },
++        "H.225",                                      /* name */
++        IP_NAT_HELPER_F_ALWAYS,                       /* flags */
++        THIS_MODULE,                                  /* module */
++        { { 0, { .tcp = { __constant_htons(H225_PORT) } } },  /* tuple */
++          { 0, { 0 }, IPPROTO_TCP } },
++        { { 0, { .tcp = { 0xFFFF } } },               /* mask */
++          { 0, { 0 }, 0xFFFF } },
++        h225_nat_help,                                /* helper */
++        h225_nat_expected                             /* expectfn */
++      };
++
++static int __init init(void)
++{
++      int ret;
++      
++      ret = ip_nat_helper_register(&h225);
++
++      if (ret != 0)
++              printk("ip_nat_h323: cannot initialize the module!\n");
++
++      return ret;
++}
++
++static void __exit fini(void)
++{
++      ip_nat_helper_unregister(&h225);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_helper.c linux-2.4.20/net/ipv4/netfilter/ip_nat_helper.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_helper.c        Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_helper.c    Wed Sep 24 09:17:50 2003
+@@ -8,6 +8,9 @@
+  *            - add support for SACK adjustment 
+  *    14 Mar 2002 Harald Welte <laforge@gnumonks.org>:
+  *            - merge SACK support into newnat API
++ *    16 Aug 2002 Brian J. Murrell <netfilter@interlinx.bc.ca>:
++ *            - make ip_nat_resize_packet more generic (TCP and UDP)
++ *            - add ip_nat_mangle_udp_packet
+  */
+ #include <linux/version.h>
+ #include <linux/config.h>
+@@ -22,6 +25,7 @@
+ #include <net/icmp.h>
+ #include <net/ip.h>
+ #include <net/tcp.h>
++#include <net/udp.h>
+ #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
+ #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)
+@@ -51,18 +55,12 @@
+                    int new_size)
+ {
+       struct iphdr *iph;
+-      struct tcphdr *tcph;
+-      void *data;
+       int dir;
+       struct ip_nat_seq *this_way, *other_way;
+       DEBUGP("ip_nat_resize_packet: old_size = %u, new_size = %u\n",
+               (*skb)->len, new_size);
+-      iph = (*skb)->nh.iph;
+-      tcph = (void *)iph + iph->ihl*4;
+-      data = (void *)tcph + tcph->doff*4;
+-
+       dir = CTINFO2DIR(ctinfo);
+       this_way = &ct->nat.info.seq[dir];
+@@ -84,37 +82,50 @@
+       }
+       iph = (*skb)->nh.iph;
+-      tcph = (void *)iph + iph->ihl*4;
+-      data = (void *)tcph + tcph->doff*4;
++      if (iph->protocol == IPPROTO_TCP) {
++              struct tcphdr *tcph = (void *)iph + iph->ihl*4;
+-      DEBUGP("ip_nat_resize_packet: Seq_offset before: ");
+-      DUMP_OFFSET(this_way);
++              DEBUGP("ip_nat_resize_packet: Seq_offset before: ");
++              DUMP_OFFSET(this_way);
+-      LOCK_BH(&ip_nat_seqofs_lock);
++              LOCK_BH(&ip_nat_seqofs_lock);
+-      /* SYN adjust. If it's uninitialized, of this is after last 
+-       * correction, record it: we don't handle more than one 
+-       * adjustment in the window, but do deal with common case of a 
+-       * retransmit */
+-      if (this_way->offset_before == this_way->offset_after
+-          || before(this_way->correction_pos, ntohl(tcph->seq))) {
+-              this_way->correction_pos = ntohl(tcph->seq);
+-              this_way->offset_before = this_way->offset_after;
+-              this_way->offset_after = (int32_t)
+-                      this_way->offset_before + new_size - (*skb)->len;
+-      }
++              /* SYN adjust. If it's uninitialized, of this is after last 
++               * correction, record it: we don't handle more than one 
++               * adjustment in the window, but do deal with common case of a 
++               * retransmit */
++              if (this_way->offset_before == this_way->offset_after
++                  || before(this_way->correction_pos, ntohl(tcph->seq))) {
++                      this_way->correction_pos = ntohl(tcph->seq);
++                      this_way->offset_before = this_way->offset_after;
++                      this_way->offset_after = (int32_t)
++                              this_way->offset_before + new_size -
++                              (*skb)->len;
++              }
+-      UNLOCK_BH(&ip_nat_seqofs_lock);
++              UNLOCK_BH(&ip_nat_seqofs_lock);
+-      DEBUGP("ip_nat_resize_packet: Seq_offset after: ");
+-      DUMP_OFFSET(this_way);
++              DEBUGP("ip_nat_resize_packet: Seq_offset after: ");
++              DUMP_OFFSET(this_way);
++      }
+       
+       return 1;
+ }
+-/* Generic function for mangling variable-length address changes inside
+- * NATed connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX command in FTP).
++/**
++ * ip_nat_mangle_tcp_packet - Mangle and potentially resize payload packet
++ * @skb: pointer to skb of packet on which we operate
++ * @ct: conntrack of the connection to which this packet belongs
++ * @ctinfo: conntrack_info of the connection to which this packet belongs
++ * @match_offset: offset in bytes where to-be-manipulated part starts
++ * @match_len: lenght of the to-be-manipulated part
++ * @rep_buffer: pointer to buffer containing replacement
++ * @rep_len: length of replacement
++ *
++ * Generic function for mangling fixed and variable-length changes inside
++ * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX command 
++ * in FTP).
+  *
+  * Takes care about all the nasty sequence number changes, checksumming,
+  * skb enlargement, ...
+@@ -174,10 +185,11 @@
+       tcph = (void *)iph + iph->ihl*4;
+       data = (void *)tcph + tcph->doff*4;
+-      /* move post-replacement */
+-      memmove(data + match_offset + rep_len,
+-               data + match_offset + match_len,
+-               (*skb)->tail - (data + match_offset + match_len));
++      if (rep_len != match_len)
++              /* move post-replacement */
++              memmove(data + match_offset + rep_len,
++                      data + match_offset + match_len,
++                      (*skb)->tail - (data + match_offset + match_len));
+       /* insert data from buffer */
+       memcpy(data + match_offset, rep_buffer, rep_len);
+@@ -207,6 +219,125 @@
+       return 1;
+ }
++                      
++/**
++ * ip_nat_mangle_udp_packet - Mangle and potentially resize payload packet
++ * @skb: pointer to skb of packet on which we operate
++ * @ct: conntrack of the connection to which this packet belongs
++ * @ctinfo: conntrack_info of the connection to which this packet belongs
++ * @match_offset: offset in bytes where to-be-manipulated part starts
++ * @match_len: lenght of the to-be-manipulated part
++ * @rep_buffer: pointer to buffer containing replacement
++ * @rep_len: length of replacement
++ *
++ * Generic function for mangling fixed and variable-length changes inside
++ * NATed TCP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX
++ * commad in the Amanda protocol)
++ *
++ * Takes care about all the nasty sequence number changes, checksumming,
++ * skb enlargement, ...
++ *
++ * FIXME: should be unified with ip_nat_mangle_tcp_packet!!
++ *
++ * */
++
++int 
++ip_nat_mangle_udp_packet(struct sk_buff **skb,
++                       struct ip_conntrack *ct,
++                       enum ip_conntrack_info ctinfo,
++                       unsigned int match_offset,
++                       unsigned int match_len,
++                       char *rep_buffer,
++                       unsigned int rep_len)
++{
++      struct iphdr *iph = (*skb)->nh.iph;
++      struct udphdr *udph = (void *)iph + iph->ihl * 4;
++      unsigned char *data;
++      u_int32_t udplen, newlen, newudplen;
++
++      udplen = (*skb)->len - iph->ihl*4;
++      newudplen = udplen - match_len + rep_len;
++      newlen = iph->ihl*4 + newudplen;
++
++      if (newlen > 65535) {
++              if (net_ratelimit())
++                      printk("ip_nat_mangle_udp_packet: nat'ed packet "
++                              "exceeds maximum packet size\n");
++              return 0;
++      }
++
++      if ((*skb)->len != newlen) {
++              if (!ip_nat_resize_packet(skb, ct, ctinfo, newlen)) {
++                      printk("resize_packet failed!!\n");
++                      return 0;
++              }
++      }
++
++      /* Alexey says: if a hook changes _data_ ... it can break
++         original packet sitting in tcp queue and this is fatal */
++      if (skb_cloned(*skb)) {
++              struct sk_buff *nskb = skb_copy(*skb, GFP_ATOMIC);
++              if (!nskb) {
++                      if (net_ratelimit())
++                              printk("Out of memory cloning TCP packet\n");
++                      return 0;
++              }
++              /* Rest of kernel will get very unhappy if we pass it
++                 a suddenly-orphaned skbuff */
++              if ((*skb)->sk)
++                      skb_set_owner_w(nskb, (*skb)->sk);
++              kfree_skb(*skb);
++              *skb = nskb;
++      }
++
++      /* skb may be copied !! */
++      iph = (*skb)->nh.iph;
++      udph = (void *)iph + iph->ihl*4;
++      data = (void *)udph + sizeof(struct udphdr);
++
++      if (rep_len != match_len)
++              /* move post-replacement */
++              memmove(data + match_offset + rep_len,
++                      data + match_offset + match_len,
++                      (*skb)->tail - (data + match_offset + match_len));
++
++      /* insert data from buffer */
++      memcpy(data + match_offset, rep_buffer, rep_len);
++
++      /* update skb info */
++      if (newlen > (*skb)->len) {
++              DEBUGP("ip_nat_mangle_udp_packet: Extending packet by "
++                      "%u to %u bytes\n", newlen - (*skb)->len, newlen);
++              skb_put(*skb, newlen - (*skb)->len);
++      } else {
++              DEBUGP("ip_nat_mangle_udp_packet: Shrinking packet from "
++                      "%u to %u bytes\n", (*skb)->len, newlen);
++              skb_trim(*skb, newlen);
++      }
++
++      /* update the length of the UDP and IP packets to the new values*/
++      udph->len = htons((*skb)->len - iph->ihl*4);
++      iph->tot_len = htons(newlen);
++
++      /* fix udp checksum if udp checksum was previously calculated */
++      if ((*skb)->csum != 0) {
++              (*skb)->csum = csum_partial((char *)udph +
++                                          sizeof(struct udphdr),
++                                          newudplen - sizeof(struct udphdr),
++                                          0);
++
++              udph->check = 0;
++              udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
++                                              newudplen, IPPROTO_UDP,
++                                              csum_partial((char *)udph,
++                                                       sizeof(struct udphdr),
++                                                      (*skb)->csum));
++      }
++
++      ip_send_check(iph);
++
++      return 1;
++}
+ /* Adjust one found SACK option including checksum correction */
+ static void
+@@ -255,54 +386,49 @@
+ }
+                       
+-/* TCP SACK sequence number adjustment, return 0 if sack found and adjusted */
+-static inline int
++/* TCP SACK sequence number adjustment. */
++static inline void
+ ip_nat_sack_adjust(struct sk_buff *skb,
+-                      struct ip_conntrack *ct,
+-                      enum ip_conntrack_info ctinfo)
++                 struct ip_conntrack *ct,
++                 enum ip_conntrack_info ctinfo)
+ {
+-      struct iphdr *iph;
+       struct tcphdr *tcph;
+-      unsigned char *ptr;
+-      int length, dir, sack_adjusted = 0;
++      unsigned char *ptr, *optend;
++      unsigned int dir;
+-      iph = skb->nh.iph;
+-      tcph = (void *)iph + iph->ihl*4;
+-      length = (tcph->doff*4)-sizeof(struct tcphdr);
++      tcph = (void *)skb->nh.iph + skb->nh.iph->ihl*4;
++      optend = (unsigned char *)tcph + tcph->doff*4;
+       ptr = (unsigned char *)(tcph+1);
+       dir = CTINFO2DIR(ctinfo);
+-      while (length > 0) {
+-              int opcode = *ptr++;
++      while (ptr < optend) {
++              int opcode = ptr[0];
+               int opsize;
+               switch (opcode) {
+               case TCPOPT_EOL:
+-                      return !sack_adjusted;
++                      return;
+               case TCPOPT_NOP:
+-                      length--;
++                      ptr++;
+                       continue;
+               default:
+-                      opsize = *ptr++;
+-                      if (opsize > length) /* no partial opts */
+-                              return !sack_adjusted;
++                      opsize = ptr[1];
++                       /* no partial opts */
++                      if (ptr + opsize > optend || opsize < 2)
++                              return;
+                       if (opcode == TCPOPT_SACK) {
+                               /* found SACK */
+                               if((opsize >= (TCPOLEN_SACK_BASE
+                                              +TCPOLEN_SACK_PERBLOCK)) &&
+                                  !((opsize - TCPOLEN_SACK_BASE)
+                                    % TCPOLEN_SACK_PERBLOCK))
+-                                      sack_adjust(tcph, ptr-2,
++                                      sack_adjust(tcph, ptr,
+                                                   &ct->nat.info.seq[!dir]);
+-                              
+-                              sack_adjusted = 1;
+                       }
+-                      ptr += opsize-2;
+-                      length -= opsize;
++                      ptr += opsize;
+               }
+       }
+-      return !sack_adjusted;
+ }
+ /* TCP sequence number adjustment */
+@@ -363,6 +489,13 @@
+ #define MODULE_MAX_NAMELEN            32
++/**
++ * ip_nat_helper_register - Register NAT application helper
++ * @me: structure describing the helper
++ *
++ * This function is called by NAT application helpers to register
++ * themselves with the NAT core.
++ */
+ int ip_nat_helper_register(struct ip_nat_helper *me)
+ {
+       int ret = 0;
+@@ -382,9 +515,9 @@
+                       const char *tmp = me->me->name;
+                       
+                       if (strlen(tmp) + 6 > MODULE_MAX_NAMELEN) {
+-                              printk(__FUNCTION__ ": unable to "
++                              printk("%s: unable to "
+                                      "compute conntrack helper name "
+-                                     "from %s\n", tmp);
++                                     "from %s\n", __FUNCTION__, tmp);
+                               return -EBUSY;
+                       }
+                       tmp += 6;
+@@ -430,6 +563,13 @@
+       return ret;
+ }
++/**
++ * ip_nat_helper_unregister - Unregister NAT application helper
++ * @me: structure describing the helper
++ *
++ * This function is called by NAT application helpers to unregister
++ * themselves from the NAT core.
++ */
+ void ip_nat_helper_unregister(struct ip_nat_helper *me)
+ {
+       int found = 0;
+@@ -467,7 +607,8 @@
+                   && ct_helper->me) {
+                       __MOD_DEC_USE_COUNT(ct_helper->me);
+               } else 
+-                      printk(__FUNCTION__ ": unable to decrement usage count"
+-                             " of conntrack helper %s\n", me->me->name);
++                      printk("%s: unable to decrement usage count"
++                             " of conntrack helper %s\n",
++                             __FUNCTION__, me->me->name);
+       }
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_irc.c linux-2.4.20/net/ipv4/netfilter/ip_nat_irc.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_irc.c   Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_irc.c       Wed Sep 24 09:16:17 2003
+@@ -243,9 +243,6 @@
+       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;
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_mms.c linux-2.4.20/net/ipv4/netfilter/ip_nat_mms.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_mms.c   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_mms.c       Wed Sep 24 09:17:48 2003
+@@ -0,0 +1,350 @@
++/* MMS extension for TCP NAT alteration.
++ * (C) 2002 by Filip Sneppe <filip.sneppe@cronos.be>
++ * based on ip_nat_ftp.c and ip_nat_irc.c
++ *
++ * ip_nat_mms.c v0.3 2002-09-22
++ *
++ *      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_mms.o ports=port1,port2,...port<MAX_PORTS>
++ *
++ *      Please give the ports of all MMS servers You wish to connect to.
++ *      If you don't specify ports, the default will be TCP port 1755.
++ *
++ *      More info on MMS protocol, firewalls and NAT:
++ *      http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/MMSFirewall.asp
++ *      http://www.microsoft.com/windows/windowsmedia/serve/firewall.asp
++ *
++ *      The SDP project people are reverse-engineering MMS:
++ *      http://get.to/sdp
++ */
++
++/* FIXME: issue with UDP & fragmentation with this URL: 
++   http://www.cnn.com/video/world/2002/01/21/jb.shoe.bomb.cafe.cnn.low.asx 
++   may be related to out-of-order first packets:
++   basically the expectation is set up correctly, then the server sends
++   a first UDP packet which is fragmented plus arrives out-of-order.
++   the MASQUERADING firewall with ip_nat_mms loaded responds with
++   an ICMP unreachable back to the server */
++
++#include <linux/module.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/ip.h>
++#include <linux/tcp.h>
++#include <net/tcp.h>
++#include <linux/netfilter_ipv4/ip_nat.h>
++#include <linux/netfilter_ipv4/ip_nat_helper.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++#include <linux/netfilter_ipv4/ip_conntrack_mms.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++
++#if 0 
++#define DEBUGP printk
++#define DUMP_BYTES(address, counter)                                \
++({                                                                  \
++      int temp_counter;                                           \
++      for(temp_counter=0; temp_counter<counter; ++temp_counter) { \
++              DEBUGP("%u ", (u8)*(address+temp_counter));         \
++      };                                                          \
++      DEBUGP("\n");                                               \
++})
++#else
++#define DEBUGP(format, args...)
++#define DUMP_BYTES(address, counter)
++#endif
++
++#define MAX_PORTS 8
++static int ports[MAX_PORTS];
++static int ports_c = 0;
++
++#ifdef MODULE_PARM
++MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
++#endif
++
++MODULE_AUTHOR("Filip Sneppe <filip.sneppe@cronos.be>");
++MODULE_DESCRIPTION("Microsoft Windows Media Services (MMS) NAT module");
++MODULE_LICENSE("GPL");
++
++DECLARE_LOCK_EXTERN(ip_mms_lock);
++
++/* FIXME: Time out? --RR */
++
++static int mms_data_fixup(const struct ip_ct_mms_expect *ct_mms_info,
++                          struct ip_conntrack *ct,
++                          struct sk_buff **pskb,
++                          enum ip_conntrack_info ctinfo,
++                          struct ip_conntrack_expect *expect)
++{
++      u_int32_t newip;
++      struct ip_conntrack_tuple t;
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
++      char *data = (char *)tcph + tcph->doff * 4;
++      int i, j, k, port;
++      u_int16_t mms_proto;
++
++      u_int32_t *mms_chunkLenLV    = (u_int32_t *)(data + MMS_SRV_CHUNKLENLV_OFFSET);
++      u_int32_t *mms_chunkLenLM    = (u_int32_t *)(data + MMS_SRV_CHUNKLENLM_OFFSET);
++      u_int32_t *mms_messageLength = (u_int32_t *)(data + MMS_SRV_MESSAGELENGTH_OFFSET);
++
++      int zero_padding;
++
++      char buffer[28];         /* "\\255.255.255.255\UDP\65635" * 2 (for unicode) */
++      char unicode_buffer[75]; /* 27*2 (unicode) + 20 + 1 */
++      char proto_string[6];
++      
++      MUST_BE_LOCKED(&ip_mms_lock);
++
++      /* what was the protocol again ? */
++      mms_proto = expect->tuple.dst.protonum;
++      sprintf(proto_string, "%u", mms_proto);
++      
++      DEBUGP("ip_nat_mms: mms_data_fixup: info (seq %u + %u) in %u, proto %s\n",
++             expect->seq, ct_mms_info->len, ntohl(tcph->seq),
++             mms_proto == IPPROTO_UDP ? "UDP"
++             : mms_proto == IPPROTO_TCP ? "TCP":proto_string);
++      
++      newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++
++      /* Alter conntrack's expectations. */
++      t = expect->tuple;
++      t.dst.ip = newip;
++      for (port = ct_mms_info->port; port != 0; port++) {
++              t.dst.u.tcp.port = htons(port);
++              if (ip_conntrack_change_expect(expect, &t) == 0) {
++                      DEBUGP("ip_nat_mms: mms_data_fixup: using port %d\n", port);
++                      break;
++              }
++      }
++      
++      if(port == 0)
++              return 0;
++
++      sprintf(buffer, "\\\\%u.%u.%u.%u\\%s\\%u",
++              NIPQUAD(newip),
++              expect->tuple.dst.protonum == IPPROTO_UDP ? "UDP"
++              : expect->tuple.dst.protonum == IPPROTO_TCP ? "TCP":proto_string,
++              port);
++      DEBUGP("ip_nat_mms: new unicode string=%s\n", buffer);
++      
++      memset(unicode_buffer, 0, sizeof(char)*75);
++
++      for (i=0; i<strlen(buffer); ++i)
++              *(unicode_buffer+i*2)=*(buffer+i);
++      
++      DEBUGP("ip_nat_mms: mms_data_fixup: padding: %u len: %u\n", ct_mms_info->padding, ct_mms_info->len);
++      DEBUGP("ip_nat_mms: mms_data_fixup: offset: %u\n", MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len);
++      DUMP_BYTES(data+MMS_SRV_UNICODE_STRING_OFFSET, 60);
++      
++      /* add end of packet to it */
++      for (j=0; j<ct_mms_info->padding; ++j) {
++              DEBUGP("ip_nat_mms: mms_data_fixup: i=%u j=%u byte=%u\n", 
++                     i, j, (u8)*(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j));
++              *(unicode_buffer+i*2+j) = *(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j);
++      }
++
++      /* pad with zeroes at the end ? see explanation of weird math below */
++      zero_padding = (8-(strlen(buffer)*2 + ct_mms_info->padding + 4)%8)%8;
++      for (k=0; k<zero_padding; ++k)
++              *(unicode_buffer+i*2+j+k)= (char)0;
++      
++      DEBUGP("ip_nat_mms: mms_data_fixup: zero_padding = %u\n", zero_padding);
++      DEBUGP("ip_nat_mms: original=> chunkLenLV=%u chunkLenLM=%u messageLength=%u\n",
++             *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength);
++      
++      /* explanation, before I forget what I did:
++         strlen(buffer)*2 + ct_mms_info->padding + 4 must be divisable by 8;
++         divide by 8 and add 3 to compute the mms_chunkLenLM field,
++         but note that things may have to be padded with zeroes to align by 8 
++         bytes, hence we add 7 and divide by 8 to get the correct length */ 
++      *mms_chunkLenLM    = (u_int32_t) (3+(strlen(buffer)*2+ct_mms_info->padding+11)/8);
++      *mms_chunkLenLV    = *mms_chunkLenLM+2;
++      *mms_messageLength = *mms_chunkLenLV*8;
++      
++      DEBUGP("ip_nat_mms: modified=> chunkLenLV=%u chunkLenLM=%u messageLength=%u\n",
++             *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength);
++      
++      ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, 
++                               expect->seq - ntohl(tcph->seq),
++                               ct_mms_info->len + ct_mms_info->padding, unicode_buffer,
++                               strlen(buffer)*2 + ct_mms_info->padding + zero_padding);
++      DUMP_BYTES(unicode_buffer, 60);
++      
++      return 1;
++}
++
++static unsigned int
++mms_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_conntrack *master = master_ct(ct);
++
++      IP_NF_ASSERT(info);
++      IP_NF_ASSERT(master);
++
++      IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
++
++      DEBUGP("ip_nat_mms: mms_nat_expected: We have a connection!\n");
++
++      newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++      newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++      DEBUGP("ip_nat_mms: mms_nat_expected: hook %s: newsrc->newdst %u.%u.%u.%u->%u.%u.%u.%u\n",
++             hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
++             : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
++             : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???",
++             NIPQUAD(newsrcip), NIPQUAD(newdstip));
++
++      if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
++              newip = newsrcip;
++      else
++              newip = newdstip;
++
++      DEBUGP("ip_nat_mms: mms_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;
++
++      return ip_nat_setup_info(ct, &mr, hooknum);
++}
++
++
++static unsigned int mms_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)
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
++      unsigned int datalen;
++      int dir;
++      struct ip_ct_mms_expect *ct_mms_info;
++
++      if (!exp)
++              DEBUGP("ip_nat_mms: no exp!!");
++
++      ct_mms_info = &exp->help.exp_mms_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("ip_nat_mms: mms_nat_help: not touching dir %s at hook %s\n",
++                     dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
++                     hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
++                     : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
++                     : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
++              return NF_ACCEPT;
++      }
++      DEBUGP("ip_nat_mms: mms_nat_help: beyond 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" : "???");
++      
++      datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
++      
++      DEBUGP("ip_nat_mms: mms_nat_help: %u+%u=%u %u %u\n", exp->seq, ct_mms_info->len,
++             exp->seq + ct_mms_info->len,
++             ntohl(tcph->seq),
++             ntohl(tcph->seq) + datalen);
++      
++      LOCK_BH(&ip_mms_lock);
++      /* Check wether the whole IP/proto/port pattern is carried in the payload */
++      if (between(exp->seq + ct_mms_info->len,
++          ntohl(tcph->seq),
++          ntohl(tcph->seq) + datalen)) {
++              if (!mms_data_fixup(ct_mms_info, ct, pskb, ctinfo, exp)) {
++                      UNLOCK_BH(&ip_mms_lock);
++                      return NF_DROP;
++              }
++      } else {
++              /* Half a match?  This means a partial retransmisison.
++                 It's a cracker being funky. */
++              if (net_ratelimit()) {
++                      printk("ip_nat_mms: partial packet %u/%u in %u/%u\n",
++                             exp->seq, ct_mms_info->len,
++                             ntohl(tcph->seq),
++                             ntohl(tcph->seq) + datalen);
++              }
++              UNLOCK_BH(&ip_mms_lock);
++              return NF_DROP;
++      }
++      UNLOCK_BH(&ip_mms_lock);
++      
++      return NF_ACCEPT;
++}
++
++static struct ip_nat_helper mms[MAX_PORTS];
++static char mms_names[MAX_PORTS][10];
++
++/* Not __exit: called from init() */
++static void fini(void)
++{
++      int i;
++
++      for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
++              DEBUGP("ip_nat_mms: unregistering helper for port %d\n", ports[i]);
++              ip_nat_helper_unregister(&mms[i]);
++      }
++}
++
++static int __init init(void)
++{
++      int i, ret = 0;
++      char *tmpname;
++
++      if (ports[0] == 0)
++              ports[0] = MMS_PORT;
++
++      for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
++
++              memset(&mms[i], 0, sizeof(struct ip_nat_helper));
++
++              mms[i].tuple.dst.protonum = IPPROTO_TCP;
++              mms[i].tuple.src.u.tcp.port = htons(ports[i]);
++              mms[i].mask.dst.protonum = 0xFFFF;
++              mms[i].mask.src.u.tcp.port = 0xFFFF;
++              mms[i].help = mms_nat_help;
++              mms[i].me = THIS_MODULE;
++              mms[i].flags = 0;
++              mms[i].expect = mms_nat_expected;
++
++              tmpname = &mms_names[i][0];
++              if (ports[i] == MMS_PORT)
++                      sprintf(tmpname, "mms");
++              else
++                      sprintf(tmpname, "mms-%d", i);
++              mms[i].name = tmpname;
++
++              DEBUGP("ip_nat_mms: register helper for port %d\n",
++                              ports[i]);
++              ret = ip_nat_helper_register(&mms[i]);
++
++              if (ret) {
++                      printk("ip_nat_mms: error registering "
++                             "helper for port %d\n", ports[i]);
++                      fini();
++                      return ret;
++              }
++              ports_c++;
++      }
++
++      return ret;
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_pptp.c linux-2.4.20/net/ipv4/netfilter/ip_nat_pptp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_pptp.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_pptp.c      Wed Sep 24 09:17:55 2003
+@@ -0,0 +1,475 @@
++/*
++ * ip_nat_pptp.c      - Version 1.5
++ *
++ * NAT support for PPTP (Point to Point Tunneling Protocol).
++ * PPTP is a a protocol for creating virtual private networks.
++ * It is a specification defined by Microsoft and some vendors
++ * working with Microsoft.  PPTP is built on top of a modified
++ * version of the Internet Generic Routing Encapsulation Protocol.
++ * GRE is defined in RFC 1701 and RFC 1702.  Documentation of
++ * PPTP can be found in RFC 2637
++ *
++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org>
++ *
++ * Development of this code funded by Astaro AG (http://www.astaro.com/)
++ *
++ * TODO: - Support for multiple calls within one session
++ *       (needs netfilter newnat code)
++ *     - NAT to a unique tuple, not to TCP source port
++ *       (needs netfilter tuple reservation)
++ *
++ * Changes:
++ *     2002-02-10 - Version 1.3
++ *       - Use ip_nat_mangle_tcp_packet() because of cloned skb's
++ *       in local connections (Philip Craig <philipc@snapgear.com>)
++ *       - add checks for magicCookie and pptp version
++ *       - make argument list of pptp_{out,in}bound_packet() shorter
++ *       - move to C99 style initializers
++ *       - print version number at module loadtime
++ *     2003-09-22 - Version 1.5
++ *       - use SNATed tcp sourceport as callid, since we get called before
++ *         TCP header is mangled (Philip Craig <philipc@snapgear.com>)
++ * 
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/tcp.h>
++#include <net/tcp.h>
++#include <linux/netfilter_ipv4/ip_nat.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++#include <linux/netfilter_ipv4/ip_nat_helper.h>
++#include <linux/netfilter_ipv4/ip_nat_pptp.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
++
++#define IP_NAT_PPTP_VERSION "1.5"
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
++MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
++
++
++#if 0
++#include "ip_conntrack_pptp_priv.h"
++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
++                                     ": " format, ## args)
++#else
++#define DEBUGP(format, args...)
++#endif
++
++static unsigned int
++pptp_nat_expected(struct sk_buff **pskb,
++                unsigned int hooknum,
++                struct ip_conntrack *ct,
++                struct ip_nat_info *info)
++{
++      struct ip_conntrack *master = master_ct(ct);
++      struct ip_nat_multi_range mr;
++      struct ip_ct_pptp_master *ct_pptp_info;
++      struct ip_nat_pptp *nat_pptp_info;
++      u_int32_t newip, newcid;
++      int ret;
++
++      IP_NF_ASSERT(info);
++      IP_NF_ASSERT(master);
++      IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
++
++      DEBUGP("we have a connection!\n");
++
++      LOCK_BH(&ip_pptp_lock);
++      ct_pptp_info = &master->help.ct_pptp_info;
++      nat_pptp_info = &master->nat.help.nat_pptp_info;
++
++      /* need to alter GRE tuple because conntrack expectfn() used 'wrong'
++       * (unmanipulated) values */
++      if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
++              DEBUGP("completing tuples with NAT info \n");
++              /* we can do this, since we're unconfirmed */
++              if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key ==
++                      htonl(ct_pptp_info->pac_call_id)) {     
++                      /* assume PNS->PAC */
++                      ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
++                              htonl(nat_pptp_info->pns_call_id);
++                      ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
++                              htonl(nat_pptp_info->pns_call_id);
++                      newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
++                      newcid = htonl(nat_pptp_info->pac_call_id);
++              } else {
++                      /* assume PAC->PNS */
++                      ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
++                              htonl(nat_pptp_info->pac_call_id);
++                      ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
++                              htonl(nat_pptp_info->pac_call_id);
++                      newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++                      newcid = htonl(nat_pptp_info->pns_call_id);
++              }
++      } else {
++              if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key ==
++                      htonl(ct_pptp_info->pac_call_id)) {     
++                      /* assume PNS->PAC */
++                      newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++                      newcid = htonl(ct_pptp_info->pns_call_id);
++              }
++              else {
++                      /* assume PAC->PNS */
++                      newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
++                      newcid = htonl(ct_pptp_info->pac_call_id);
++              }
++      }
++
++      mr.rangesize = 1;
++      mr.range[0].flags = IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED;
++      mr.range[0].min_ip = mr.range[0].max_ip = newip;
++      mr.range[0].min = mr.range[0].max = 
++              ((union ip_conntrack_manip_proto ) { newcid }); 
++      DEBUGP("change ip to %u.%u.%u.%u\n", 
++              NIPQUAD(newip));
++      DEBUGP("change key to 0x%x\n", ntohl(newcid));
++      ret = ip_nat_setup_info(ct, &mr, hooknum);
++
++      UNLOCK_BH(&ip_pptp_lock);
++
++      return ret;
++
++}
++
++/* outbound packets == from PNS to PAC */
++static inline unsigned int
++pptp_outbound_pkt(struct sk_buff **pskb,
++                struct ip_conntrack *ct,
++                enum ip_conntrack_info ctinfo,
++                struct ip_conntrack_expect *exp)
++
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct tcphdr *tcph = (void *) iph + iph->ihl*4;
++      struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *) 
++                                      ((void *)tcph + tcph->doff*4);
++
++      struct PptpControlHeader *ctlh;
++      union pptp_ctrl_union pptpReq;
++      struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
++      struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
++
++      u_int16_t msg, *cid = NULL, new_callid;
++
++      /* FIXME: size checks !!! */
++      ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
++      pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
++
++      new_callid = htons(ct_pptp_info->pns_call_id);
++      
++      switch (msg = ntohs(ctlh->messageType)) {
++              case PPTP_OUT_CALL_REQUEST:
++                      cid = &pptpReq.ocreq->callID;
++                      /* FIXME: ideally we would want to reserve a call ID
++                       * here.  current netfilter NAT core is not able to do
++                       * this :( For now we use TCP source port. This breaks
++                       * multiple calls within one control session */
++
++                      /* save original call ID in nat_info */
++                      nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id;
++
++                      /* don't use tcph->source since we are at a DSTmanip
++                       * hook (e.g. PREROUTING) and pkt is not mangled yet */
++                      new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port;
++
++                      /* save new call ID in ct info */
++                      ct_pptp_info->pns_call_id = ntohs(new_callid);
++                      break;
++              case PPTP_IN_CALL_REPLY:
++                      cid = &pptpReq.icreq->callID;
++                      break;
++              case PPTP_CALL_CLEAR_REQUEST:
++                      cid = &pptpReq.clrreq->callID;
++                      break;
++              default:
++                      DEBUGP("unknown outbound packet 0x%04x:%s\n", msg,
++                            (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
++                      /* fall through */
++
++              case PPTP_SET_LINK_INFO:
++                      /* only need to NAT in case PAC is behind NAT box */
++              case PPTP_START_SESSION_REQUEST:
++              case PPTP_START_SESSION_REPLY:
++              case PPTP_STOP_SESSION_REQUEST:
++              case PPTP_STOP_SESSION_REPLY:
++              case PPTP_ECHO_REQUEST:
++              case PPTP_ECHO_REPLY:
++                      /* no need to alter packet */
++                      return NF_ACCEPT;
++      }
++
++      IP_NF_ASSERT(cid);
++
++      DEBUGP("altering call id from 0x%04x to 0x%04x\n",
++              ntohs(*cid), ntohs(new_callid));
++
++      /* mangle packet */
++      ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)cid - (void *)pptph,
++                               sizeof(new_callid), (char *)&new_callid,
++                               sizeof(new_callid));
++
++      return NF_ACCEPT;
++}
++
++/* inbound packets == from PAC to PNS */
++static inline unsigned int
++pptp_inbound_pkt(struct sk_buff **pskb,
++               struct ip_conntrack *ct,
++               enum ip_conntrack_info ctinfo,
++               struct ip_conntrack_expect *oldexp)
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct tcphdr *tcph = (void *) iph + iph->ihl*4;
++      struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *) 
++                                      ((void *)tcph + tcph->doff*4);
++
++      struct PptpControlHeader *ctlh;
++      union pptp_ctrl_union pptpReq;
++      struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
++      struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
++
++      u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL;
++      u_int32_t old_dst_ip;
++
++      struct ip_conntrack_tuple t, inv_t;
++      struct ip_conntrack_tuple *orig_t, *reply_t;
++
++      /* FIXME: size checks !!! */
++      ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
++      pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
++
++      new_pcid = htons(nat_pptp_info->pns_call_id);
++
++      switch (msg = ntohs(ctlh->messageType)) {
++      case PPTP_OUT_CALL_REPLY:
++              pcid = &pptpReq.ocack->peersCallID;     
++              cid = &pptpReq.ocack->callID;
++              if (!oldexp) {
++                      DEBUGP("outcall but no expectation\n");
++                      break;
++              }
++              old_dst_ip = oldexp->tuple.dst.ip;
++              t = oldexp->tuple;
++              invert_tuplepr(&inv_t, &t);
++
++              /* save original PAC call ID in nat_info */
++              nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
++
++              /* alter expectation */
++              orig_t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++              reply_t = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
++              if (t.src.ip == orig_t->src.ip && t.dst.ip == orig_t->dst.ip) {
++                      /* expectation for PNS->PAC direction */
++                      t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id);
++                      t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id);
++                      inv_t.src.ip = reply_t->src.ip;
++                      inv_t.dst.ip = reply_t->dst.ip;
++                      inv_t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id);
++                      inv_t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id);
++              } else {
++                      /* expectation for PAC->PNS direction */
++                      t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id);
++                      t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id);
++                      inv_t.src.ip = orig_t->src.ip;
++                      inv_t.dst.ip = orig_t->dst.ip;
++                      inv_t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id);
++                      inv_t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id);
++              }
++
++              if (!ip_conntrack_change_expect(oldexp, &t)) {
++                      DEBUGP("successfully changed expect\n");
++              } else {
++                      DEBUGP("can't change expect\n");
++              }
++              ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig, &t);
++              ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, &inv_t);
++              break;
++      case PPTP_IN_CALL_CONNECT:
++              pcid = &pptpReq.iccon->peersCallID;
++              if (!oldexp)
++                      break;
++              old_dst_ip = oldexp->tuple.dst.ip;
++              t = oldexp->tuple;
++
++              /* alter expectation, no need for callID */
++              if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) {
++                      /* expectation for PNS->PAC direction */
++                      t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++              } else {
++                      /* expectation for PAC->PNS direction */
++                      t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++              }
++
++              if (!ip_conntrack_change_expect(oldexp, &t)) {
++                      DEBUGP("successfully changed expect\n");
++              } else {
++                      DEBUGP("can't change expect\n");
++              }
++              break;
++      case PPTP_IN_CALL_REQUEST:
++              /* only need to nat in case PAC is behind NAT box */
++              break;
++      case PPTP_WAN_ERROR_NOTIFY:
++              pcid = &pptpReq.wanerr->peersCallID;
++              break;
++      case PPTP_CALL_DISCONNECT_NOTIFY:
++              pcid = &pptpReq.disc->callID;
++              break;
++
++      default:
++              DEBUGP("unknown inbound packet %s\n",
++                      (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
++              /* fall through */
++
++      case PPTP_START_SESSION_REQUEST:
++      case PPTP_START_SESSION_REPLY:
++      case PPTP_STOP_SESSION_REQUEST:
++      case PPTP_STOP_SESSION_REPLY:
++      case PPTP_ECHO_REQUEST:
++      case PPTP_ECHO_REPLY:
++              /* no need to alter packet */
++              return NF_ACCEPT;
++      }
++
++      /* mangle packet */
++      IP_NF_ASSERT(pcid);
++      DEBUGP("altering peer call id from 0x%04x to 0x%04x\n",
++              ntohs(*pcid), ntohs(new_pcid));
++      ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)pcid - (void *)pptph,
++                               sizeof(new_pcid), (char *)&new_pcid, 
++                               sizeof(new_pcid));
++
++      if (new_cid) {
++              IP_NF_ASSERT(cid);
++              DEBUGP("altering call id from 0x%04x to 0x%04x\n",
++                      ntohs(*cid), ntohs(new_cid));
++              ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, 
++                                       (void *)cid - (void *)pptph, 
++                                       sizeof(new_cid), (char *)&new_cid, 
++                                       sizeof(new_cid));
++      }
++
++      /* great, at least we don't need to resize packets */
++      return NF_ACCEPT;
++}
++
++
++static unsigned int tcp_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)
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct tcphdr *tcph = (void *) iph + iph->ihl*4;
++      unsigned int datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4;
++      struct pptp_pkt_hdr *pptph;
++
++      int dir;
++
++      DEBUGP("entering\n");
++
++      /* Only mangle things once: DST for original direction
++         and SRC for reply direction. */
++      dir = CTINFO2DIR(ctinfo);
++      if (!((HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
++           && dir == IP_CT_DIR_ORIGINAL)
++            || (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST
++                && dir == IP_CT_DIR_REPLY))) {
++              DEBUGP("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"
++                     : hooknum == NF_IP_LOCAL_IN ? "INPUT" : "???");
++              return NF_ACCEPT;
++      }
++
++      /* if packet is too small, just skip it */
++      if (datalen < sizeof(struct pptp_pkt_hdr)+
++                    sizeof(struct PptpControlHeader)) {
++              DEBUGP("pptp packet too short\n");
++              return NF_ACCEPT;       
++      }
++
++      pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4);
++
++      /* if it's not a control message, we can't handle it */
++      if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
++          ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
++              DEBUGP("not a pptp control packet\n");
++              return NF_ACCEPT;
++      }
++
++      LOCK_BH(&ip_pptp_lock);
++
++      if (dir == IP_CT_DIR_ORIGINAL) {
++              /* reuqests sent by client to server (PNS->PAC) */
++              pptp_outbound_pkt(pskb, ct, ctinfo, exp);
++      } else {
++              /* response from the server to the client (PAC->PNS) */
++              pptp_inbound_pkt(pskb, ct, ctinfo, exp);
++      }
++
++      UNLOCK_BH(&ip_pptp_lock);
++
++      return NF_ACCEPT;
++}
++
++/* nat helper struct for control connection */
++static struct ip_nat_helper pptp_tcp_helper = { 
++      .list = { NULL, NULL },
++      .name = "pptp", 
++      .flags = IP_NAT_HELPER_F_ALWAYS, 
++      .me = THIS_MODULE,
++      .tuple = { .src = { .ip = 0, 
++                          .u = { .tcp = { .port = 
++                                      __constant_htons(PPTP_CONTROL_PORT) } 
++                               } 
++                        },
++                 .dst = { .ip = 0, 
++                          .u = { .all = 0 }, 
++                          .protonum = IPPROTO_TCP 
++                        } 
++               },
++
++      .mask = { .src = { .ip = 0, 
++                         .u = { .tcp = { .port = 0xFFFF } } 
++                       },
++                .dst = { .ip = 0, 
++                         .u = { .all = 0 }, 
++                         .protonum = 0xFFFF 
++                       } 
++              },
++      .help = tcp_help, 
++      .expect = pptp_nat_expected 
++};
++
++                        
++static int __init init(void)
++{
++      DEBUGP("%s: registering NAT helper\n", __FILE__);
++      if (ip_nat_helper_register(&pptp_tcp_helper)) {
++              printk(KERN_ERR "Unable to register NAT application helper "
++                              "for pptp\n");
++              return -EIO;
++      }
++
++      printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION);
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      DEBUGP("cleanup_module\n" );
++      ip_nat_helper_unregister(&pptp_tcp_helper);
++      printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_proto_gre.c linux-2.4.20/net/ipv4/netfilter/ip_nat_proto_gre.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_proto_gre.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_proto_gre.c Wed Sep 24 09:17:55 2003
+@@ -0,0 +1,225 @@
++/*
++ * ip_nat_proto_gre.c - Version 1.2
++ *
++ * NAT protocol helper module for GRE.
++ *
++ * GRE is a generic encapsulation protocol, which is generally not very
++ * suited for NAT, as it has no protocol-specific part as port numbers.
++ *
++ * It has an optional key field, which may help us distinguishing two 
++ * connections between the same two hosts.
++ *
++ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 
++ *
++ * PPTP is built on top of a modified version of GRE, and has a mandatory
++ * field called "CallID", which serves us for the same purpose as the key
++ * field in plain GRE.
++ *
++ * Documentation about PPTP can be found in RFC 2637
++ *
++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org>
++ *
++ * Development of this code funded by Astaro AG (http://www.astaro.com/)
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/netfilter_ipv4/ip_nat.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++#include <linux/netfilter_ipv4/ip_nat_protocol.h>
++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
++MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE");
++
++#if 0
++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
++                                     ": " format, ## args)
++#else
++#define DEBUGP(x, args...)
++#endif
++
++/* is key in given range between min and max */
++static int
++gre_in_range(const struct ip_conntrack_tuple *tuple,
++           enum ip_nat_manip_type maniptype,
++           const union ip_conntrack_manip_proto *min,
++           const union ip_conntrack_manip_proto *max)
++{
++      u_int32_t key;
++
++      if (maniptype == IP_NAT_MANIP_SRC)
++              key = tuple->src.u.gre.key;
++      else
++              key = tuple->dst.u.gre.key;
++
++      return ntohl(key) >= ntohl(min->gre.key)
++              && ntohl(key) <= ntohl(max->gre.key);
++}
++
++/* generate unique tuple ... */
++static int 
++gre_unique_tuple(struct ip_conntrack_tuple *tuple,
++               const struct ip_nat_range *range,
++               enum ip_nat_manip_type maniptype,
++               const struct ip_conntrack *conntrack)
++{
++      u_int32_t min, i, range_size;
++      u_int32_t key = 0, *keyptr;
++
++      if (maniptype == IP_NAT_MANIP_SRC)
++              keyptr = &tuple->src.u.gre.key;
++      else
++              keyptr = &tuple->dst.u.gre.key;
++
++      if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
++
++              switch (tuple->dst.u.gre.version) {
++              case 0:
++                      DEBUGP("NATing GRE version 0 (ct=%p)\n",
++                              conntrack);
++                      min = 1;
++                      range_size = 0xffffffff;
++                      break;
++              case GRE_VERSION_PPTP:
++                      DEBUGP("%p: NATing GRE PPTP\n", 
++                              conntrack);
++                      min = 1;
++                      range_size = 0xffff;
++                      break;
++              default:
++                      printk(KERN_WARNING "nat_gre: unknown GRE version\n");
++                      return 0;
++                      break;
++              }
++
++      } else {
++              min = ntohl(range->min.gre.key);
++              range_size = ntohl(range->max.gre.key) - min + 1;
++      }
++
++      DEBUGP("min = %u, range_size = %u\n", min, range_size); 
++
++      for (i = 0; i < range_size; i++, key++) {
++              *keyptr = htonl(min + key % range_size);
++              if (!ip_nat_used_tuple(tuple, conntrack))
++                      return 1;
++      }
++
++      DEBUGP("%p: no NAT mapping\n", conntrack);
++
++      return 0;
++}
++
++/* manipulate a GRE packet according to maniptype */
++static void 
++gre_manip_pkt(struct iphdr *iph, size_t len, 
++            const struct ip_conntrack_manip *manip,
++            enum ip_nat_manip_type maniptype)
++{
++      struct gre_hdr *greh = (struct gre_hdr *)((u_int32_t *)iph+iph->ihl);
++      struct gre_hdr_pptp *pgreh = (struct gre_hdr_pptp *) greh;
++
++      /* we only have destination manip of a packet, since 'source key' 
++       * is not present in the packet itself */
++      if (maniptype == IP_NAT_MANIP_DST) {
++              /* key manipulation is always dest */
++              switch (greh->version) {
++              case 0:
++                      if (!greh->key) {
++                              DEBUGP("can't nat GRE w/o key\n");
++                              break;
++                      }
++                      if (greh->csum) {
++                              /* FIXME: Never tested this code... */
++                              *(gre_csum(greh)) = 
++                                      ip_nat_cheat_check(~*(gre_key(greh)),
++                                                      manip->u.gre.key,
++                                                      *(gre_csum(greh)));
++                      }
++                      *(gre_key(greh)) = manip->u.gre.key;
++                      break;
++              case GRE_VERSION_PPTP:
++                      DEBUGP("call_id -> 0x%04x\n", 
++                              ntohl(manip->u.gre.key));
++                      pgreh->call_id = htons(ntohl(manip->u.gre.key));
++                      break;
++              default:
++                      DEBUGP("can't nat unknown GRE version\n");
++                      break;
++              }
++      }
++}
++
++/* print out a nat tuple */
++static unsigned int 
++gre_print(char *buffer, 
++        const struct ip_conntrack_tuple *match,
++        const struct ip_conntrack_tuple *mask)
++{
++      unsigned int len = 0;
++
++      if (mask->dst.u.gre.version)
++              len += sprintf(buffer + len, "version=%d ",
++                              ntohs(match->dst.u.gre.version));
++
++      if (mask->dst.u.gre.protocol)
++              len += sprintf(buffer + len, "protocol=0x%x ",
++                              ntohs(match->dst.u.gre.protocol));
++
++      if (mask->src.u.gre.key)
++              len += sprintf(buffer + len, "srckey=0x%x ", 
++                              ntohl(match->src.u.gre.key));
++
++      if (mask->dst.u.gre.key)
++              len += sprintf(buffer + len, "dstkey=0x%x ",
++                              ntohl(match->src.u.gre.key));
++
++      return len;
++}
++
++/* print a range of keys */
++static unsigned int 
++gre_print_range(char *buffer, const struct ip_nat_range *range)
++{
++      if (range->min.gre.key != 0 
++          || range->max.gre.key != 0xFFFF) {
++              if (range->min.gre.key == range->max.gre.key)
++                      return sprintf(buffer, "key 0x%x ",
++                                      ntohl(range->min.gre.key));
++              else
++                      return sprintf(buffer, "keys 0x%u-0x%u ",
++                                      ntohl(range->min.gre.key),
++                                      ntohl(range->max.gre.key));
++      } else
++              return 0;
++}
++
++/* nat helper struct */
++static struct ip_nat_protocol gre = 
++      { { NULL, NULL }, "GRE", IPPROTO_GRE,
++        gre_manip_pkt,
++        gre_in_range,
++        gre_unique_tuple,
++        gre_print,
++        gre_print_range 
++      };
++                                
++static int __init init(void)
++{
++        if (ip_nat_protocol_register(&gre))
++                return -EIO;
++
++        return 0;
++}
++
++static void __exit fini(void)
++{
++        ip_nat_protocol_unregister(&gre);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_quake3.c linux-2.4.20/net/ipv4/netfilter/ip_nat_quake3.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_quake3.c        Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_quake3.c    Wed Sep 24 09:17:58 2003
+@@ -0,0 +1,249 @@
++/* Quake3 extension for UDP NAT alteration.
++ * (C) 2002 by Filip Sneppe <filip.sneppe@cronos.be>
++ * based on ip_nat_ftp.c and ip_nat_tftp.c
++ *
++ * ip_nat_quake3.c v0.0.3 2002-08-31
++ *
++ *      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_quake3.o ports=port1,port2,...port<MAX_PORTS>
++ *
++ *      please give the ports of all Quake3 master servers You wish to
++ *      connect to. If you don't specify ports, the default will be UDP
++ *      port 27950.
++ *
++ *      Thanks to the Ethereal folks for their analysis of the Quake3 protocol.
++ *
++ *      Notes: 
++ *      - If you're one of those people who would try anything to lower
++ *        latency while playing Quake (and who isn't :-) ), you may want to
++ *        consider not loading ip_nat_quake3 at all and just MASQUERADE all
++ *        outgoing UDP traffic.
++ *        This will make ip_conntrack_quake3 add the necessary expectations,
++ *        but there will be no overhead for client->server UDP streams. If
++ *        ip_nat_quake3 is loaded, quake3_nat_expected will be called per NAT
++ *        hook for every packet in the client->server UDP stream.
++ *      - Only SNAT/MASQUEARDE targets are useful for ip_nat_quake3.
++ *        The IP addresses in the master connection payload (=IP addresses
++ *        of Quake servers) have no relation with the master server so
++ *        DNAT'ing the master connection to a server should not change the
++ *        expected connections.
++ *      - Not tested due to lack of equipment:
++ *        - multiple Quake3 clients behind one MASQUERADE gateway
++ *        - what if Quake3 client is running on router too
++ */
++
++#include <linux/module.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/ip.h>
++#include <linux/udp.h>
++
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_quake3.h>
++#include <linux/netfilter_ipv4/ip_nat_helper.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++
++MODULE_AUTHOR("Filip Sneppe <filip.sneppe@cronos.be>");
++MODULE_DESCRIPTION("Netfilter NAT helper for Quake III Arena");
++MODULE_LICENSE("GPL");
++
++#define MAX_PORTS 8
++
++static int ports[MAX_PORTS];
++static int ports_c = 0;
++#ifdef MODULE_PARM
++MODULE_PARM(ports,"1-" __MODULE_STRING(MAX_PORTS) "i");
++MODULE_PARM_DESC(ports, "port numbers of Quake III master servers");
++#endif
++
++/* Quake3 master server reply will add > 100 expectations per reply packet; when
++   doing lots of printk's, klogd may not be able to read /proc/kmsg fast enough */
++#if 0 
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++static struct quake3_search quake3s_nat = { "****", "getserversResponse", sizeof("getserversResponse") - 1 };
++
++static unsigned int 
++quake3_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)
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct udphdr *udph = (void *)iph + iph->ihl * 4;
++      struct ip_conntrack_tuple repl;
++      int dir = CTINFO2DIR(ctinfo);
++      int i;
++      
++      DEBUGP("ip_nat_quake3: quake3_nat_help, direction: %s 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" : "???"
++            );
++      DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++      DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
++      
++      /* Only mangle things once: original direction in POST_ROUTING
++         and reply direction on PRE_ROUTING. */
++      if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
++          || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
++              DEBUGP("ip_nat_quake3: 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) {
++              DEBUGP("no conntrack expectation to modify\n");
++              return NF_ACCEPT;
++      }
++
++      if (strnicmp((const char *)udph + 12, quake3s_nat.pattern, quake3s_nat.plen) == 0) {
++              for(i=31; /* 8 bytes UDP hdr, 4 bytes filler, 18 bytes "getserversResponse", 1 byte "\" */
++                  i+6 < ntohs(udph->len);
++                  i+=7) {
++                      DEBUGP("ip_nat_quake3: adding server at offset %u/%u %u.%u.%u.%u:%u\n", 
++                             i, ntohs(udph->len),
++                             NIPQUAD( (u_int32_t) *( (u_int32_t *)( (int)udph + i ) ) ),
++                             ntohs((__u16) *( (__u16 *)( (int)udph + i + 4 ) ) ) );
++                      
++                      memset(&repl, 0, sizeof(repl));
++
++                      repl.dst.protonum = IPPROTO_UDP;
++                      repl.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++                      repl.dst.ip = *( (u_int32_t *)( (int)udph + i ) );
++                      repl.dst.u.udp.port = (__u16) *( (__u16 *)( (int)udph + i + 4 )  );
++                      
++                      ip_conntrack_change_expect(exp, &repl);
++              }
++      }
++      return NF_ACCEPT;
++}
++
++static unsigned int 
++quake3_nat_expected(struct sk_buff **pskb,
++                    unsigned int hooknum,
++                    struct ip_conntrack *ct, 
++                    struct ip_nat_info *info) 
++{
++      const struct ip_conntrack *master = ct->master->expectant;
++      struct ip_nat_multi_range mr;
++      u_int32_t newsrcip, newdstip, newip;
++#if 0 
++      const struct ip_conntrack_tuple *repl =
++              &master->tuplehash[IP_CT_DIR_REPLY].tuple;
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct udphdr *udph = (void *)iph + iph->ihl*4;
++#endif
++
++      DEBUGP("ip_nat_quake3: quake3_nat_expected: here we are\n");
++      DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++
++      IP_NF_ASSERT(info);
++      IP_NF_ASSERT(master);
++      IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
++      
++      newdstip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
++      newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++      
++      if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) {
++              newip = newsrcip;
++              DEBUGP("hook: %s orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u "
++                     "newsrc: %u.%u.%u.%u\n",
++                     hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
++                     : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
++                     : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "????",
++                     NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source),
++                     NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest),
++                     NIPQUAD(newip));
++              
++      } else {
++              newip = newdstip;
++              DEBUGP("hook: %s orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u "
++                     "newdst: %u.%u.%u.%u\n",
++                     hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
++                     : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
++                     : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "????",
++                     NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source),
++                     NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest),
++                     NIPQUAD(newip));
++      }
++      
++      mr.rangesize = 1;
++      mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
++      mr.range[0].min_ip = mr.range[0].max_ip = newip; 
++
++      return ip_nat_setup_info(ct,&mr,hooknum);
++}
++
++static struct ip_nat_helper quake3[MAX_PORTS];
++static char quake3_names[MAX_PORTS][13];  /* quake3-65535 */
++
++static void fini(void)
++{
++      int i;
++      
++      for (i = 0 ; i < ports_c; i++) {
++              DEBUGP("ip_nat_quake3: unregistering helper for port %d\n", ports[i]);
++                     ip_nat_helper_unregister(&quake3[i]);
++      }
++}
++
++static int __init init(void)
++      {
++              int i, ret = 0;
++              char *tmpname;
++
++              if (!ports[0])
++                      ports[0] = QUAKE3_MASTER_PORT;
++              
++              for (i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) {
++                      memset(&quake3[i], 0, sizeof(struct ip_nat_helper));
++
++                      quake3[i].tuple.dst.protonum = IPPROTO_UDP;
++                      quake3[i].tuple.src.u.udp.port = htons(ports[i]);
++                      quake3[i].mask.dst.protonum = 0xFFFF;
++                      quake3[i].mask.src.u.udp.port = 0xFFFF;
++                      quake3[i].help = quake3_nat_help;
++                      quake3[i].flags = 0;
++                      quake3[i].me = THIS_MODULE;
++                      quake3[i].expect = quake3_nat_expected;
++                      
++                      tmpname = &quake3_names[i][0];
++                      if (ports[i] == QUAKE3_MASTER_PORT)
++                              sprintf(tmpname, "quake3");
++                      else
++                              sprintf(tmpname, "quake3-%d", i);
++                      quake3[i].name = tmpname;
++                      
++                      DEBUGP("ip_nat_quake3: registering helper for port %d: name %s\n",
++                             ports[i], quake3[i].name);
++                      ret = ip_nat_helper_register(&quake3[i]);
++                      
++                      if (ret) {
++                              printk("ip_nat_quake3: unable to register helper for port %d\n",
++                                     ports[i]);
++                              fini();
++                              return ret;
++                      }
++                      ports_c++;
++              }
++              return ret;
++      }
++      
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_rule.c linux-2.4.20/net/ipv4/netfilter/ip_nat_rule.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_rule.c  Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_rule.c      Wed Sep 24 09:16:27 2003
+@@ -1,5 +1,4 @@
+ /* Everything about the rules for NAT. */
+-#define __NO_VERSION__
+ #include <linux/types.h>
+ #include <linux/ip.h>
+ #include <linux/netfilter.h>
+@@ -231,7 +230,7 @@
+       return 1;
+ }
+-static inline unsigned int
++inline unsigned int
+ alloc_null_binding(struct ip_conntrack *conntrack,
+                  struct ip_nat_info *info,
+                  unsigned int hooknum)
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_snmp_basic.c linux-2.4.20/net/ipv4/netfilter/ip_nat_snmp_basic.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_snmp_basic.c    Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_snmp_basic.c        Wed Sep 24 09:16:21 2003
+@@ -53,9 +53,9 @@
+ #include <linux/brlock.h>
+ #include <linux/types.h>
+ #include <linux/ip.h>
++#include <net/checksum.h>
+ #include <net/udp.h>
+ #include <asm/uaccess.h>
+-#include <asm/checksum.h>
+@@ -1309,9 +1309,9 @@
+       "snmp",
+       IP_NAT_HELPER_F_STANDALONE,
+       THIS_MODULE,
+-      { { 0, { __constant_htons(SNMP_PORT) } },
++      { { 0, { .udp = { __constant_htons(SNMP_PORT) } } },
+         { 0, { 0 }, IPPROTO_UDP } },
+-      { { 0, { 0xFFFF } },
++      { { 0, { .udp = { 0xFFFF } } },
+         { 0, { 0 }, 0xFFFF } },
+       nat_help, NULL };
+  
+@@ -1320,9 +1320,9 @@
+       "snmp_trap",
+       IP_NAT_HELPER_F_STANDALONE,
+       THIS_MODULE,
+-      { { 0, { __constant_htons(SNMP_TRAP_PORT) } },
++      { { 0, { .udp = { __constant_htons(SNMP_TRAP_PORT) } } },
+         { 0, { 0 }, IPPROTO_UDP } },
+-      { { 0, { 0xFFFF } },
++      { { 0, { .udp = { 0xFFFF } } },
+         { 0, { 0 }, 0xFFFF } },
+       nat_help, NULL };
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_standalone.c linux-2.4.20/net/ipv4/netfilter/ip_nat_standalone.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_standalone.c    Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_standalone.c        Wed Sep 24 09:17:50 2003
+@@ -109,19 +109,12 @@
+               }
+               /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
+       case IP_CT_NEW:
+-#ifdef CONFIG_IP_NF_NAT_LOCAL
+-              /* LOCAL_IN hook doesn't have a chain and thus doesn't care
+-               * about new packets -HW */
+-              if (hooknum == NF_IP_LOCAL_IN)
+-                      return NF_ACCEPT;
+-#endif
+               info = &ct->nat.info;
+               WRITE_LOCK(&ip_nat_lock);
+               /* Seen it before?  This can happen for loopback, retrans,
+                  or local packets.. */
+               if (!(info->initialized & (1 << maniptype))) {
+-                      int in_hashes = info->initialized;
+                       unsigned int ret;
+                       if (ct->master
+@@ -130,6 +123,13 @@
+                               ret = call_expect(master_ct(ct), pskb, 
+                                                 hooknum, ct, info);
+                       } else {
++#ifdef CONFIG_IP_NF_NAT_LOCAL
++                              /* LOCAL_IN hook doesn't have a chain!  */
++                              if (hooknum == NF_IP_LOCAL_IN)
++                                      ret = alloc_null_binding(ct, info,
++                                                               hooknum);
++                              else
++#endif
+                               ret = ip_nat_rule_find(pskb, hooknum, in, out,
+                                                      ct, info);
+                       }
+@@ -138,13 +138,6 @@
+                               WRITE_UNLOCK(&ip_nat_lock);
+                               return ret;
+                       }
+-
+-                      if (in_hashes) {
+-                              IP_NF_ASSERT(info->bysource.conntrack);
+-                              replace_in_hashes(ct, info);
+-                      } else {
+-                              place_in_hashes(ct, info);
+-                      }
+               } else
+                       DEBUGP("Already setup manip %s for ct %p\n",
+                              maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",
+@@ -195,6 +188,7 @@
+       return ip_nat_fn(hooknum, pskb, in, out, okfn);
+ }
++#ifdef CONFIG_IP_NF_NAT_LOCAL
+ static unsigned int
+ ip_nat_local_fn(unsigned int hooknum,
+               struct sk_buff **pskb,
+@@ -220,6 +214,7 @@
+               return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP;
+       return ret;
+ }
++#endif
+ /* We must be after connection tracking and before packet filtering. */
+@@ -229,16 +224,23 @@
+ /* After packet filtering, change source */
+ static struct nf_hook_ops ip_nat_out_ops
+ = { { NULL, NULL }, ip_nat_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_NAT_SRC};
++
++#ifdef CONFIG_IP_NF_NAT_LOCAL
+ /* Before packet filtering, change destination */
+ static struct nf_hook_ops ip_nat_local_out_ops
+ = { { NULL, NULL }, ip_nat_local_fn, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_NAT_DST };
+-
+-#ifdef CONFIG_IP_NF_NAT_LOCAL
++/* After packet filtering, change source for reply packets of LOCAL_OUT DNAT */
+ static struct nf_hook_ops ip_nat_local_in_ops
+ = { { NULL, NULL }, ip_nat_fn, PF_INET, NF_IP_LOCAL_IN, NF_IP_PRI_NAT_SRC };
+ #endif
+-/* Protocol registration. */
++/**
++ * ip_nat_protocol_register - Register a layer 4 protocol helper
++ * @proto: structure describing this helper
++ * 
++ * This function is called by NAT layer 4 protocol helpers to register
++ * themselvers with the NAT core.
++ */
+ int ip_nat_protocol_register(struct ip_nat_protocol *proto)
+ {
+       int ret = 0;
+@@ -261,9 +263,16 @@
+       return ret;
+ }
+-/* Noone stores the protocol anywhere; simply delete it. */
++/**
++ * ip_nat_protocol_unregister - Unregister a layer 4 protocol helper
++ * @proto: sturcture describing the helper
++ *
++ * This function is called by NAT layer 4 protocol helpers to
++ * unregister themselves from the NAT core.
++ */
+ void ip_nat_protocol_unregister(struct ip_nat_protocol *proto)
+ {
++      /* Noone stores the protocol anywhere; simply delete it. */
+       WRITE_LOCK(&ip_nat_lock);
+       LIST_DELETE(&protos, proto);
+       WRITE_UNLOCK(&ip_nat_lock);
+@@ -301,12 +310,12 @@
+               printk("ip_nat_init: can't register out hook.\n");
+               goto cleanup_inops;
+       }
++#ifdef CONFIG_IP_NF_NAT_LOCAL
+       ret = nf_register_hook(&ip_nat_local_out_ops);
+       if (ret < 0) {
+               printk("ip_nat_init: can't register local out hook.\n");
+               goto cleanup_outops;
+       }
+-#ifdef CONFIG_IP_NF_NAT_LOCAL
+       ret = nf_register_hook(&ip_nat_local_in_ops);
+       if (ret < 0) {
+               printk("ip_nat_init: can't register local in hook.\n");
+@@ -323,9 +332,9 @@
+ #ifdef CONFIG_IP_NF_NAT_LOCAL
+       nf_unregister_hook(&ip_nat_local_in_ops);
+  cleanup_localoutops:
+-#endif
+       nf_unregister_hook(&ip_nat_local_out_ops);
+  cleanup_outops:
++#endif
+       nf_unregister_hook(&ip_nat_out_ops);
+  cleanup_inops:
+       nf_unregister_hook(&ip_nat_in_ops);
+@@ -358,5 +367,6 @@
+ EXPORT_SYMBOL(ip_nat_helper_unregister);
+ EXPORT_SYMBOL(ip_nat_cheat_check);
+ EXPORT_SYMBOL(ip_nat_mangle_tcp_packet);
++EXPORT_SYMBOL(ip_nat_mangle_udp_packet);
+ EXPORT_SYMBOL(ip_nat_used_tuple);
+ MODULE_LICENSE("GPL");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_talk.c linux-2.4.20/net/ipv4/netfilter/ip_nat_talk.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_talk.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_talk.c      Wed Sep 24 09:18:08 2003
+@@ -0,0 +1,473 @@
++/* 
++ * talk extension for UDP NAT alteration. 
++ * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ **
++ *     Module load syntax:
++ *     insmod ip_nat_talk.o talk=[0|1] ntalk=[0|1] ntalk2=[0|1]
++ *
++ *            talk=[0|1]      disable|enable old talk support
++ *           ntalk=[0|1]      disable|enable ntalk support
++ *          ntalk2=[0|1]      disable|enable ntalk2 support
++ *
++ *     The default is talk=1 ntalk=1 ntalk2=1
++ *
++ *  
++ */
++#include <linux/module.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/ip.h>
++#include <linux/udp.h>
++#include <linux/kernel.h>
++#include <net/tcp.h>
++#include <net/udp.h>
++
++#include <linux/netfilter_ipv4/ip_nat.h>
++#include <linux/netfilter_ipv4/ip_nat_helper.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++#include <linux/netfilter_ipv4/ip_conntrack_talk.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++
++/* Default all talk protocols are supported */
++static int talk   = 1;
++static int ntalk  = 1;
++static int ntalk2 = 1;
++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
++MODULE_DESCRIPTION("talk network address translation module");
++#ifdef MODULE_PARM
++MODULE_PARM(talk, "i");
++MODULE_PARM_DESC(talk, "support (old) talk protocol");
++MODULE_PARM(ntalk, "i");
++MODULE_PARM_DESC(ntalk, "support ntalk protocol");
++MODULE_PARM(ntalk2, "i");
++MODULE_PARM_DESC(ntalk2, "support ntalk2 protocol");
++#endif
++
++#if 0
++#define DEBUGP printk
++#define IP_NAT_TALK_DEBUG
++#else
++#define DEBUGP(format, args...)
++#endif
++
++/* FIXME: Time out? --RR */
++
++static int
++mangle_packet(struct sk_buff **pskb,
++            struct ip_conntrack *ct,
++            u_int32_t newip,
++            u_int16_t port,
++            struct talk_addr *addr,
++            struct talk_addr *ctl_addr)
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct udphdr *udph = (void *)iph + iph->ihl * 4;
++      size_t udplen = (*pskb)->len - iph->ihl * 4;
++
++      /* Fortunately talk sends a structure with the address and
++         port in it. The size of the packet won't change. */
++
++      if (ctl_addr == NULL) {
++              /* response */
++              if (addr->ta_addr == INADDR_ANY)
++                      return 1;
++              DEBUGP("ip_nat_talk_mangle_packet: response orig %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", 
++                     NIPQUAD(addr->ta_addr), ntohs(addr->ta_port),
++                     NIPQUAD(newip), ntohs(port));
++              addr->ta_addr = newip;
++              addr->ta_port = port;
++      } else {
++              /* message */
++              if (addr->ta_addr != INADDR_ANY) {
++                      /* Change address inside packet to match way we're mapping
++                         this connection. */
++                      DEBUGP("ip_nat_talk_mangle_packet: message orig addr %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", 
++                             NIPQUAD(addr->ta_addr), ntohs(addr->ta_port),
++                             NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), 
++                             ntohs(addr->ta_port));
++                      addr->ta_addr = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++              }
++              DEBUGP("ip_nat_talk_mangle_packet: message orig ctl_addr %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", 
++                     NIPQUAD(ctl_addr->ta_addr), ntohs(ctl_addr->ta_port),
++                     NIPQUAD(newip), ntohs(port));
++              ctl_addr->ta_addr = newip;
++              ctl_addr->ta_port = port;
++      }
++
++      /* Fix checksums */
++      (*pskb)->csum = csum_partial((char *)udph + sizeof(struct udphdr), udplen - sizeof(struct udphdr), 0);
++      udph->check = 0;
++      udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP,
++                                      csum_partial((char *)udph, sizeof(struct udphdr), (*pskb)->csum));
++              
++      ip_send_check(iph);
++      return 1;
++}
++
++static int talk_help_msg(struct ip_conntrack *ct,
++                       struct sk_buff **pskb,
++                       u_char type,
++                       struct talk_addr *addr,
++                       struct talk_addr *ctl_addr)
++{
++      u_int32_t newip;
++      u_int16_t port;
++      
++      unsigned int verdict = NF_ACCEPT;
++
++      DEBUGP("ip_nat_talk_help_msg: addr: %u.%u.%u.%u:%u, ctl_addr: %u.%u.%u.%u:%u, type %d\n",
++              NIPQUAD(addr->ta_addr), ntohs(addr->ta_port),
++              NIPQUAD(ctl_addr->ta_addr), ntohs(ctl_addr->ta_port),
++              type);
++
++      /* Change address inside packet to match way we're mapping
++         this connection. */
++      newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++      port  = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port;
++      DEBUGP("ip_nat_talk_help_msg: inserting: %u.%u.%u.%u:%u\n",
++              NIPQUAD(newip), ntohs(port));
++
++      if (!mangle_packet(pskb, ct, newip, port, addr, ctl_addr))
++              verdict = NF_DROP;
++
++      return verdict;
++}
++
++static int talk_help_response(struct ip_conntrack *ct,
++                            struct ip_conntrack_expect *exp,
++                            struct sk_buff **pskb,
++                            u_char type,
++                            u_char answer,
++                            struct talk_addr *addr)
++{
++      u_int32_t newip;
++      u_int16_t port;
++      struct ip_conntrack_tuple t;
++      struct ip_ct_talk_expect *ct_talk_info;
++
++      DEBUGP("ip_nat_talk_help_response: addr: %u.%u.%u.%u:%u, type %d answer %d\n",
++              NIPQUAD(addr->ta_addr), ntohs(addr->ta_port),
++              type, answer);
++      
++      LOCK_BH(&ip_talk_lock);
++      ct_talk_info = &exp->help.exp_talk_info;
++
++      if (!(answer == SUCCESS 
++            && (type == LOOK_UP || type == ANNOUNCE)
++            && exp != NULL)) {
++              UNLOCK_BH(&ip_talk_lock);
++              return NF_ACCEPT;
++      }
++              
++      DEBUGP("ip_nat_talk_help_response: talkinfo port %u (%s)\n", 
++              ntohs(ct_talk_info->port), 
++              type == LOOK_UP ? "LOOK_UP" : "ANNOUNCE");
++
++      /* Change address inside packet to match way we're mapping
++         this connection. */
++      newip = ct->tuplehash[type == LOOK_UP ? IP_CT_DIR_ORIGINAL : 
++                                              IP_CT_DIR_REPLY].tuple.dst.ip;
++      /* We can read expect here without conntrack lock, since it's
++         only set in ip_conntrack_talk , with ip_talk_lock held
++         writable */ 
++      t = exp->tuple;
++      t.dst.ip = newip;
++
++      /* Try to get same port: if not, try to change it. */
++      for (port = ntohs(ct_talk_info->port); port != 0; port++) {
++              if (type == LOOK_UP)
++                      t.dst.u.tcp.port = htons(port);
++              else
++                      t.dst.u.udp.port = htons(port);
++
++              if (ip_conntrack_change_expect(exp, &t) == 0) {
++                      DEBUGP("ip_nat_talk_help_response: using %u.%u.%u.%u:%u\n", NIPQUAD(newip), port);
++                      break;
++              }
++      }
++      UNLOCK_BH(&ip_talk_lock);
++
++      if (port == 0 || !mangle_packet(pskb, ct, newip, htons(port), addr, NULL))
++              return NF_DROP;
++      
++      return NF_ACCEPT;
++}
++
++static unsigned int talk_help(struct ip_conntrack *ct,
++                            struct ip_conntrack_expect *exp,
++                            struct ip_nat_info *info,
++                            enum ip_conntrack_info ctinfo,
++                            unsigned int hooknum,
++                            struct sk_buff **pskb,
++                            int talk_port)
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct udphdr *udph = (void *)iph + iph->ihl * 4;
++      unsigned int udplen = (*pskb)->len - iph->ihl * 4;
++      char *data = (char *)udph + sizeof(struct udphdr);
++      int dir;
++
++      /* Only mangle things once: original direction in POST_ROUTING
++         and reply direction on PRE_ROUTING. */
++      dir = CTINFO2DIR(ctinfo);
++      if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
++            || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
++              DEBUGP("ip_nat_talk_help: Not touching dir %s at hook %s\n",
++                     dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
++                     hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
++                     : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
++                     : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
++              return NF_ACCEPT;
++      }
++      DEBUGP("ip_nat_talk_help: dir %s at hook %s, %u.%u.%u.%u:%u->%u.%u.%u.%u:%u, talk port %d\n",
++             dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
++             hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
++             : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
++             : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???",
++             NIPQUAD(iph->saddr), ntohs(udph->source),
++             NIPQUAD(iph->daddr), ntohs(udph->dest),
++             talk_port);
++
++      /* Because conntrack does not drop packets, checking must be repeated here... */
++      if (talk_port == TALK_PORT) {
++              if (dir == IP_CT_DIR_ORIGINAL
++                  && udplen == sizeof(struct udphdr) + sizeof(struct talk_msg))
++                      return talk_help_msg(ct, pskb,
++                                           ((struct talk_msg *)data)->type, 
++                                           &(((struct talk_msg *)data)->addr),
++                                           &(((struct talk_msg *)data)->ctl_addr));
++              else if (dir == IP_CT_DIR_REPLY
++                       && udplen == sizeof(struct udphdr) + sizeof(struct talk_response))
++                      return talk_help_response(ct, exp, pskb,
++                                                ((struct talk_response *)data)->type, 
++                                                ((struct talk_response *)data)->answer,
++                                                &(((struct talk_response *)data)->addr));
++              else {  
++                      DEBUGP("ip_nat_talk_help: not talk %s, datalen %u != %u\n",
++                             dir == IP_CT_DIR_ORIGINAL ? "message" : "response", 
++                             (unsigned)udplen - sizeof(struct udphdr), 
++                             dir == IP_CT_DIR_ORIGINAL ? sizeof(struct talk_msg) : sizeof(struct talk_response));
++                      return NF_DROP;
++              }
++      } else {
++              if (dir == IP_CT_DIR_ORIGINAL) {
++                      if (ntalk
++                          && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_msg)
++                          && ((struct ntalk_msg *)data)->vers == NTALK_VERSION)
++                              return talk_help_msg(ct, pskb,
++                                                   ((struct ntalk_msg *)data)->type, 
++                                                   &(((struct ntalk_msg *)data)->addr),
++                                                   &(((struct ntalk_msg *)data)->ctl_addr));
++                      else if (ntalk2
++                               && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_msg)
++                               && ((struct ntalk2_msg *)data)->vers == NTALK2_VERSION
++                               && udplen == sizeof(struct udphdr) 
++                                            + sizeof(struct ntalk2_msg) 
++                                            + ((struct ntalk2_msg *)data)->extended)
++                              return talk_help_msg(ct, pskb,
++                                                   ((struct ntalk2_msg *)data)->type, 
++                                                   &(((struct ntalk2_msg *)data)->addr),
++                                                   &(((struct ntalk2_msg *)data)->ctl_addr));
++                      else {
++                              DEBUGP("ip_nat_talk_help: not ntalk/ntalk2 message, datalen %u != %u or %u + max 256\n", 
++                                     (unsigned)udplen - sizeof(struct udphdr), 
++                                     sizeof(struct ntalk_msg), sizeof(struct ntalk2_msg));
++                              return NF_DROP;
++                      }
++              } else {
++                      if (ntalk
++                          && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_response)
++                          && ((struct ntalk_response *)data)->vers == NTALK_VERSION)
++                              return talk_help_response(ct, exp, pskb,
++                                                        ((struct ntalk_response *)data)->type, 
++                                                        ((struct ntalk_response *)data)->answer,
++                                                        &(((struct ntalk_response *)data)->addr));
++                      else if (ntalk2
++                               && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_response)
++                               && ((struct ntalk2_response *)data)->vers == NTALK2_VERSION)
++                              return talk_help_response(ct, exp, pskb,
++                                                        ((struct ntalk2_response *)data)->type, 
++                                                        ((struct ntalk2_response *)data)->answer,
++                                                        &(((struct ntalk2_response *)data)->addr));
++                      else {
++                              DEBUGP("ip_nat_talk_help: not ntalk/ntalk2 response, datalen %u != %u or %u + max 256\n", 
++                                     (unsigned)udplen - sizeof(struct udphdr), 
++                                     sizeof(struct ntalk_response), sizeof(struct ntalk2_response));
++                              return NF_DROP;
++                      }
++              }
++      }
++}
++
++static unsigned int help(struct ip_conntrack *ct,
++                       struct ip_conntrack_expect *exp,
++                       struct ip_nat_info *info,
++                       enum ip_conntrack_info ctinfo,
++                       unsigned int hooknum,
++                       struct sk_buff **pskb)
++{
++      return talk_help(ct, exp, info, ctinfo, hooknum, pskb, TALK_PORT);
++}
++
++static unsigned int nhelp(struct ip_conntrack *ct,
++                        struct ip_conntrack_expect *exp,
++                        struct ip_nat_info *info,
++                        enum ip_conntrack_info ctinfo,
++                        unsigned int hooknum,
++                        struct sk_buff **pskb)
++{
++      return talk_help(ct, exp, info, ctinfo, hooknum, pskb, NTALK_PORT);
++}
++
++static unsigned int
++talk_nat_expected(struct sk_buff **pskb,
++                unsigned int hooknum,
++                struct ip_conntrack *ct,
++                struct ip_nat_info *info);
++
++static struct ip_nat_helper talk_helpers[2] = 
++      { { { NULL, NULL },
++            "talk",                                   /* name */
++            IP_NAT_HELPER_F_ALWAYS,                   /* flags */
++            THIS_MODULE,                              /* module */
++            { { 0, { .udp = { __constant_htons(TALK_PORT) } } }, /* tuple */
++              { 0, { 0 }, IPPROTO_UDP } },
++            { { 0, { .udp = { 0xFFFF } } },           /* mask */
++              { 0, { 0 }, 0xFFFF } },
++            help,                                     /* helper */
++            talk_nat_expected },                      /* expectfn */
++        { { NULL, NULL },
++            "ntalk",                                  /* name */
++            IP_NAT_HELPER_F_ALWAYS,                   /* flags */
++            THIS_MODULE,                                      /* module */
++            { { 0, { .udp = { __constant_htons(NTALK_PORT) } } }, /* tuple */
++              { 0, { 0 }, IPPROTO_UDP } },
++            { { 0, { .udp = { 0xFFFF } } },           /* mask */
++              { 0, { 0 }, 0xFFFF } },
++            nhelp,                                    /* helper */
++            talk_nat_expected }                               /* expectfn */
++      };
++          
++static unsigned int
++talk_nat_expected(struct sk_buff **pskb,
++                unsigned int hooknum,
++                struct ip_conntrack *ct,
++                struct ip_nat_info *info)
++{
++      struct ip_nat_multi_range mr;
++      u_int32_t newdstip, newsrcip, newip;
++      u_int16_t port;
++      unsigned int ret;
++      
++      struct ip_conntrack *master = master_ct(ct);
++
++      IP_NF_ASSERT(info);
++      IP_NF_ASSERT(master);
++
++      IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
++
++      DEBUGP("ip_nat_talk_expected: We have a connection!\n");
++
++      LOCK_BH(&ip_talk_lock);
++      port = ct->master->help.exp_talk_info.port;
++      UNLOCK_BH(&ip_talk_lock);
++
++      DEBUGP("ip_nat_talk_expected: dir %s at hook %s, ct %p, master %p\n",
++             CTINFO2DIR((*pskb)->nfct - ct->infos) == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
++             hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
++             : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
++             : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???",
++             ct, master);
++
++      if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) {
++              /* Callee client -> caller server */
++#ifdef IP_NAT_TALK_DEBUG
++              struct iphdr *iph = (*pskb)->nh.iph;
++              struct udphdr *udph = (void *)iph + iph->ihl * 4;
++
++              DEBUGP("ip_nat_talk_expected: UDP %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
++                     NIPQUAD(iph->saddr), ntohs(udph->source),
++                     NIPQUAD(iph->daddr), ntohs(udph->dest));
++#endif
++              newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++              newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
++              DEBUGP("ip_nat_talk_expected: callee client -> caller server, newsrc: %u.%u.%u.%u, newdst: %u.%u.%u.%u\n",
++                     NIPQUAD(newsrcip), NIPQUAD(newdstip));
++      } else {
++              /* Callee client -> caller client */
++#ifdef IP_NAT_TALK_DEBUG
++              struct iphdr *iph = (*pskb)->nh.iph;
++              struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
++
++              DEBUGP("ip_nat_talk_expected: TCP %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
++                     NIPQUAD(iph->saddr), ntohs(tcph->source),
++                     NIPQUAD(iph->daddr), ntohs(tcph->dest));
++#endif
++              newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
++              newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++              DEBUGP("ip_nat_talk_expected: callee client -> caller client, newsrc: %u.%u.%u.%u, newdst: %u.%u.%u.%u\n",
++                     NIPQUAD(newsrcip), NIPQUAD(newdstip));
++      }
++      if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
++              newip = newsrcip;
++      else
++              newip = newdstip;
++
++      DEBUGP("ip_nat_talk_expected: IP to %u.%u.%u.%u, port %u\n", NIPQUAD(newip), ntohs(port));
++
++      mr.rangesize = 1;
++      /* We don't want to manip the per-protocol, just the IPs... */
++      mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
++      mr.range[0].min_ip = mr.range[0].max_ip = newip;
++      
++      /* ... unless we're doing a MANIP_DST, in which case, make
++         sure we map to the correct port */
++      if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
++              mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
++              mr.range[0].min = mr.range[0].max
++                      = ((union ip_conntrack_manip_proto)
++                              { .udp = { port } });
++      }
++      ret = ip_nat_setup_info(ct, &mr, hooknum);
++
++      if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) {
++              DEBUGP("talk_expected: setting NAT helper for %p\n", ct);
++              /* NAT expectfn called with ip_nat_lock write-locked */
++              info->helper = &talk_helpers[htons(port) - TALK_PORT];
++      }
++      return ret;
++}
++
++static int __init init(void)
++{
++      int ret = 0;
++
++      if (talk > 0) {
++              ret = ip_nat_helper_register(&talk_helpers[0]);
++
++              if (ret != 0)
++                      return ret;
++      }
++      if (ntalk > 0 || ntalk2 > 0) {
++              ret = ip_nat_helper_register(&talk_helpers[1]);
++
++              if (ret != 0 && talk > 0)
++                      ip_nat_helper_unregister(&talk_helpers[0]);
++      }
++      return ret;
++}
++
++static void __exit fini(void)
++{
++      if (talk > 0)
++              ip_nat_helper_unregister(&talk_helpers[0]);
++      if (ntalk > 0 || ntalk2 > 0)
++              ip_nat_helper_unregister(&talk_helpers[1]);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_nat_tftp.c linux-2.4.20/net/ipv4/netfilter/ip_nat_tftp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_nat_tftp.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_nat_tftp.c      Wed Sep 24 09:16:24 2003
+@@ -0,0 +1,195 @@
++/*
++ * Licensed under GNU GPL version 2 Copyright Magnus Boden <mb@ozaba.mine.nu>
++ * Version: 0.0.7
++ *
++ * Thu 21 Mar 2002 Harald Welte <laforge@gnumonks.org>
++ *    - Port to newnat API
++ *
++ * This module currently supports DNAT:
++ * iptables -t nat -A PREROUTING -d x.x.x.x -j DNAT --to-dest x.x.x.y
++ *
++ * and SNAT:
++ * iptables -t nat -A POSTROUTING { -j MASQUERADE , -j SNAT --to-source x.x.x.x }
++ *
++ * It has not been tested with
++ * -j SNAT --to-source x.x.x.x-x.x.x.y since I only have one external ip
++ * If you do test this please let me know if it works or not.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/ip.h>
++#include <linux/udp.h>
++
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_tftp.h>
++#include <linux/netfilter_ipv4/ip_nat_helper.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++
++MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>");
++MODULE_DESCRIPTION("Netfilter NAT helper for tftp");
++MODULE_LICENSE("GPL");
++
++#define MAX_PORTS 8
++
++static int ports[MAX_PORTS];
++static int ports_c = 0;
++#ifdef MODULE_PARM
++MODULE_PARM(ports,"1-" __MODULE_STRING(MAX_PORTS) "i");
++MODULE_PARM_DESC(ports, "port numbers of tftp servers");
++#endif
++
++#if 0
++#define DEBUGP(format, args...) printk(__FILE__ ":" __FUNCTION__ ": " \
++                                     format, ## args)
++#else
++#define DEBUGP(format, args...)
++#endif
++static unsigned int 
++tftp_nat_help(struct ip_conntrack *ct,
++            struct ip_conntrack_expect *exp,
++            struct ip_nat_info *info,
++            enum ip_conntrack_info ctinfo,
++            unsigned int hooknum,
++            struct sk_buff **pskb)
++{
++      int dir = CTINFO2DIR(ctinfo);
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct udphdr *udph = (void *)iph + iph->ihl * 4;
++      struct tftphdr *tftph = (void *)udph + 8;
++      struct ip_conntrack_tuple repl;
++
++      if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
++            || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) 
++              return NF_ACCEPT;
++
++      if (!exp) {
++              DEBUGP("no conntrack expectation to modify\n");
++              return NF_ACCEPT;
++      }
++
++      switch (ntohs(tftph->opcode)) {
++      /* RRQ and WRQ works the same way */
++      case TFTP_OPCODE_READ:
++      case TFTP_OPCODE_WRITE:
++              repl = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
++              DEBUGP("");
++              DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++              DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
++              DEBUGP("expecting: ");
++              DUMP_TUPLE(&repl);
++              DUMP_TUPLE(&exp->mask);
++              ip_conntrack_change_expect(exp, &repl);
++              break;
++      default:
++              DEBUGP("Unknown opcode\n");
++      }               
++
++      return NF_ACCEPT;
++}
++
++static unsigned int 
++tftp_nat_expected(struct sk_buff **pskb,
++                unsigned int hooknum,
++                struct ip_conntrack *ct, 
++                struct ip_nat_info *info) 
++{
++      const struct ip_conntrack *master = ct->master->expectant;
++      const struct ip_conntrack_tuple *orig = 
++                      &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++      struct ip_nat_multi_range mr;
++#if 0
++      const struct ip_conntrack_tuple *repl =
++                      &master->tuplehash[IP_CT_DIR_REPLY].tuple;
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct udphdr *udph = (void *)iph + iph->ihl*4;
++#endif
++
++      IP_NF_ASSERT(info);
++      IP_NF_ASSERT(master);
++      IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
++
++      mr.rangesize = 1;
++      mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
++
++      if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) {
++              mr.range[0].min_ip = mr.range[0].max_ip = orig->dst.ip; 
++              DEBUGP("orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u "
++                      "newsrc: %u.%u.%u.%u\n",
++                        NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source),
++                      NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest),
++                      NIPQUAD(orig->dst.ip));
++      } else {
++              mr.range[0].min_ip = mr.range[0].max_ip = orig->src.ip;
++              mr.range[0].min.udp.port = mr.range[0].max.udp.port = 
++                                                      orig->src.u.udp.port;
++              mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
++
++              DEBUGP("orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u "
++                      "newdst: %u.%u.%u.%u:%u\n",
++                        NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source),
++                        NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest),
++                        NIPQUAD(orig->src.ip), ntohs(orig->src.u.udp.port));
++      }
++
++      return ip_nat_setup_info(ct,&mr,hooknum);
++}
++
++static struct ip_nat_helper tftp[MAX_PORTS];
++static char tftp_names[MAX_PORTS][10];
++
++static void fini(void)
++{
++      int i;
++
++      for (i = 0 ; i < ports_c; i++) {
++              DEBUGP("unregistering helper for port %d\n", ports[i]);
++              ip_nat_helper_unregister(&tftp[i]);
++      }
++}
++
++static int __init init(void)
++{
++      int i, ret = 0;
++      char *tmpname;
++
++      if (!ports[0])
++              ports[0] = TFTP_PORT;
++
++      for (i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) {
++              tftp[i].tuple.dst.protonum = IPPROTO_UDP;
++              tftp[i].tuple.src.u.udp.port = htons(ports[i]);
++              tftp[i].mask.dst.protonum = 0xFFFF;
++              tftp[i].mask.src.u.udp.port = 0xFFFF;
++              tftp[i].help = tftp_nat_help;
++              tftp[i].flags = 0;
++              tftp[i].me = THIS_MODULE;
++              tftp[i].expect = tftp_nat_expected;
++
++              tmpname = &tftp_names[i][0];
++              if (ports[i] == TFTP_PORT)
++                      sprintf(tmpname, "tftp");
++              else
++                      sprintf(tmpname, "tftp-%d", i);
++              tftp[i].name = tmpname;
++              
++              DEBUGP("ip_nat_tftp: registering for port %d: name %s\n",
++                      ports[i], tftp[i].name);
++              ret = ip_nat_helper_register(&tftp[i]);
++
++              if (ret) {
++                      printk("ip_nat_tftp: unable to register for port %d\n",
++                              ports[i]);
++                      fini();
++                      return ret;
++              }
++              ports_c++;
++      }
++      return ret;
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_pool.c linux-2.4.20/net/ipv4/netfilter/ip_pool.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_pool.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ip_pool.c  Wed Sep 24 09:16:59 2003
+@@ -0,0 +1,332 @@
++/* Kernel module for IP pool management */
++
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_pool.h>
++#include <linux/errno.h>
++#include <asm/uaccess.h>
++#include <asm/bitops.h>
++#include <linux/interrupt.h>
++#include <linux/spinlock.h>
++
++#if 0
++#define DP printk
++#else
++#define DP(format, args...)
++#endif
++
++MODULE_LICENSE("GPL");
++
++#define NR_POOL 16
++static int nr_pool = NR_POOL;/* overwrite this when loading module */
++
++struct ip_pool {
++      u_int32_t first_ip;     /* host byte order, included in range */
++      u_int32_t last_ip;      /* host byte order, included in range */
++      void *members;          /* the bitmap proper */
++      int nr_use;             /* total nr. of tests through this */
++      int nr_match;           /* total nr. of matches through this */
++      rwlock_t lock;
++};
++
++static struct ip_pool *POOL;
++
++static inline struct ip_pool *lookup(ip_pool_t index)
++{
++      if (index < 0 || index >= nr_pool) {
++              DP("ip_pool:lookup: bad index %d\n", index);
++              return 0;
++      }
++      return POOL+index;
++}
++
++int ip_pool_match(ip_pool_t index, u_int32_t addr)
++{
++        struct ip_pool *pool = lookup(index);
++      int res = 0;
++
++      if (!pool || !pool->members)
++              return 0;
++      read_lock_bh(&pool->lock);
++      if (pool->members) {
++              if (addr >= pool->first_ip && addr <= pool->last_ip) {
++                      addr -= pool->first_ip;
++                      if (test_bit(addr, pool->members)) {
++                              res = 1;
++#ifdef CONFIG_IP_POOL_STATISTICS
++                              pool->nr_match++;
++#endif
++                      }
++              }
++#ifdef CONFIG_IP_POOL_STATISTICS
++              pool->nr_use++;
++#endif
++      }
++      read_unlock_bh(&pool->lock);
++      return res;
++}
++
++static int pool_change(ip_pool_t index, u_int32_t addr, int isdel)
++{
++      struct ip_pool *pool;
++      int res = -1;
++
++      pool = lookup(index);
++      if (    !pool || !pool->members
++           || addr < pool->first_ip || addr > pool->last_ip)
++              return -1;
++      read_lock_bh(&pool->lock);
++      if (pool->members && addr >= pool->first_ip && addr <= pool->last_ip) {
++              addr -= pool->first_ip;
++              res = isdel
++                      ? (0 != test_and_clear_bit(addr, pool->members))
++                      : (0 != test_and_set_bit(addr, pool->members));
++      }
++      read_unlock_bh(&pool->lock);
++      return res;
++}
++
++int ip_pool_mod(ip_pool_t index, u_int32_t addr, int isdel)
++{
++      int res = pool_change(index,addr,isdel);
++
++      if (!isdel) res = !res;
++      return res;
++}
++
++static inline int bitmap_bytes(u_int32_t a, u_int32_t b)
++{
++      return 4*((((b-a+8)/8)+3)/4);
++}
++
++static inline int poolbytes(ip_pool_t index)
++{
++      struct ip_pool *pool = lookup(index);
++
++      return pool ? bitmap_bytes(pool->first_ip, pool->last_ip) : 0;
++}
++
++static int setpool(
++      struct sock *sk,
++      int optval,
++      void *user,
++      unsigned int len
++) {
++      struct ip_pool_request req;
++
++      DP("ip_pool:setpool: optval=%d, user=%p, len=%d\n", optval, user, len);
++      if (!capable(CAP_NET_ADMIN))
++              return -EPERM;
++      if (optval != SO_IP_POOL)
++              return -EBADF;
++      if (len != sizeof(req))
++              return -EINVAL;
++      if (copy_from_user(&req, user, sizeof(req)) != 0)
++              return -EFAULT;
++      printk("obsolete op - upgrade your ippool(8) utility.\n");
++      return -EINVAL;
++}
++
++static int getpool(
++      struct sock *sk,
++      int optval,
++      void *user,
++      int *len
++) {
++      struct ip_pool_request req;
++      struct ip_pool *pool;
++      ip_pool_t i;
++      int newbytes;
++      void *newmembers;
++      int res;
++
++      DP("ip_pool:getpool: optval=%d, user=%p\n", optval, user);
++      if (!capable(CAP_NET_ADMIN))
++              return -EINVAL;
++      if (optval != SO_IP_POOL)
++              return -EINVAL;
++      if (*len != sizeof(req)) {
++              return -EFAULT;
++      }
++      if (copy_from_user(&req, user, sizeof(req)) != 0)
++              return -EFAULT;
++      DP("ip_pool:getpool op=%d, index=%d\n", req.op, req.index);
++      if (req.op < IP_POOL_BAD001) {
++              printk("obsolete op - upgrade your ippool(8) utility.\n");
++              return -EFAULT;
++      }
++      switch(req.op) {
++      case IP_POOL_HIGH_NR:
++              DP("ip_pool HIGH_NR\n");
++              req.index = IP_POOL_NONE;
++              for (i=0; i<nr_pool; i++)
++                      if (POOL[i].members)
++                              req.index = i;
++              return copy_to_user(user, &req, sizeof(req));
++      case IP_POOL_LOOKUP:
++              DP("ip_pool LOOKUP\n");
++              pool = lookup(req.index);
++              if (!pool)
++                      return -EINVAL;
++              if (!pool->members)
++                      return -EBADF;
++              req.addr = htonl(pool->first_ip);
++              req.addr2 = htonl(pool->last_ip);
++              return copy_to_user(user, &req, sizeof(req));
++      case IP_POOL_USAGE:
++              DP("ip_pool USE\n");
++              pool = lookup(req.index);
++              if (!pool)
++                      return -EINVAL;
++              if (!pool->members)
++                      return -EBADF;
++              req.addr = pool->nr_use;
++              req.addr2 = pool->nr_match;
++              return copy_to_user(user, &req, sizeof(req));
++      case IP_POOL_TEST_ADDR:
++              DP("ip_pool TEST 0x%08x\n", req.addr);
++              pool = lookup(req.index);
++              if (!pool)
++                      return -EINVAL;
++              res = 0;
++              read_lock_bh(&pool->lock);
++              if (!pool->members) {
++                      DP("ip_pool TEST_ADDR no members in pool\n");
++                      res = -EBADF;
++                      goto unlock_and_return_res;
++              }
++              req.addr = ntohl(req.addr);
++              if (req.addr < pool->first_ip) {
++                      DP("ip_pool TEST_ADDR address < pool bounds\n");
++                      res = -ERANGE;
++                      goto unlock_and_return_res;
++              }
++              if (req.addr > pool->last_ip) {
++                      DP("ip_pool TEST_ADDR address > pool bounds\n");
++                      res = -ERANGE;
++                      goto unlock_and_return_res;
++              }
++              req.addr = (0 != test_bit((req.addr - pool->first_ip),
++                                      pool->members));
++              read_unlock_bh(&pool->lock);
++              return copy_to_user(user, &req, sizeof(req));
++      case IP_POOL_FLUSH:
++              DP("ip_pool FLUSH not yet implemented.\n");
++              return -EBUSY;
++      case IP_POOL_DESTROY:
++              DP("ip_pool DESTROY not yet implemented.\n");
++              return -EBUSY;
++      case IP_POOL_INIT:
++              DP("ip_pool INIT 0x%08x-0x%08x\n", req.addr, req.addr2);
++              pool = lookup(req.index);
++              if (!pool)
++                      return -EINVAL;
++              req.addr = ntohl(req.addr);
++              req.addr2 = ntohl(req.addr2);
++              if (req.addr > req.addr2) {
++                      DP("ip_pool INIT bad ip range\n");
++                      return -EINVAL;
++              }
++              newbytes = bitmap_bytes(req.addr, req.addr2);
++              newmembers = kmalloc(newbytes, GFP_KERNEL);
++              if (!newmembers) {
++                      DP("ip_pool INIT out of mem for %d bytes\n", newbytes);
++                      return -ENOMEM;
++              }
++              memset(newmembers, 0, newbytes);
++              write_lock_bh(&pool->lock);
++              if (pool->members) {
++                      DP("ip_pool INIT pool %d exists\n", req.index);
++                      kfree(newmembers);
++                      res = -EBUSY;
++                      goto unlock_and_return_res;
++              }
++              pool->first_ip = req.addr;
++              pool->last_ip = req.addr2;
++              pool->nr_use = 0;
++              pool->nr_match = 0;
++              pool->members = newmembers;
++              write_unlock_bh(&pool->lock);
++              return 0;
++      case IP_POOL_ADD_ADDR:
++              DP("ip_pool ADD_ADDR 0x%08x\n", req.addr);
++              req.addr = pool_change(req.index, ntohl(req.addr), 0);
++              return copy_to_user(user, &req, sizeof(req));
++      case IP_POOL_DEL_ADDR:
++              DP("ip_pool DEL_ADDR 0x%08x\n", req.addr);
++              req.addr = pool_change(req.index, ntohl(req.addr), 1);
++              return copy_to_user(user, &req, sizeof(req));
++      default:
++              DP("ip_pool:getpool bad op %d\n", req.op);
++              return -EINVAL;
++      }
++      return -EINVAL;
++
++unlock_and_return_res:
++      if (pool)
++              read_unlock_bh(&pool->lock);
++      return res;
++}
++
++static struct nf_sockopt_ops so_pool
++= { { NULL, NULL }, PF_INET,
++    SO_IP_POOL, SO_IP_POOL+1, &setpool,
++    SO_IP_POOL, SO_IP_POOL+1, &getpool,
++    0, NULL };
++
++MODULE_PARM(nr_pool, "i");
++
++static int __init init(void)
++{
++      ip_pool_t i;
++      int res;
++
++      if (nr_pool < 1) {
++              printk("ip_pool module init: bad nr_pool %d\n", nr_pool);
++              return -EINVAL;
++      }
++      POOL = kmalloc(nr_pool * sizeof(*POOL), GFP_KERNEL);
++      if (!POOL) {
++              printk("ip_pool module init: out of memory for nr_pool %d\n",
++                      nr_pool);
++              return -ENOMEM;
++      }
++      for (i=0; i<nr_pool; i++) {
++              POOL[i].first_ip = 0;
++              POOL[i].last_ip = 0;
++              POOL[i].members = 0;
++              POOL[i].nr_use = 0;
++              POOL[i].nr_match = 0;
++              POOL[i].lock = RW_LOCK_UNLOCKED;
++      }
++      res = nf_register_sockopt(&so_pool);
++      DP("ip_pool:init %d pools, result %d\n", nr_pool, res);
++      if (res != 0) {
++              kfree(POOL);
++              POOL = 0;
++      }
++      return res;
++}
++
++static void __exit fini(void)
++{
++      ip_pool_t i;
++
++      DP("ip_pool:fini BYEBYE\n");
++      nf_unregister_sockopt(&so_pool);
++      for (i=0; i<nr_pool; i++) {
++              if (POOL[i].members) {
++                      kfree(POOL[i].members);
++                      POOL[i].members = 0;
++              }
++      }
++      kfree(POOL);
++      POOL = 0;
++      DP("ip_pool:fini these are the famous last words\n");
++      return;
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_queue.c linux-2.4.20/net/ipv4/netfilter/ip_queue.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_queue.c     Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_queue.c Wed Sep 24 09:16:14 2003
+@@ -300,8 +300,9 @@
+       write_lock_bh(&queue_lock);
+       
+       if (!peer_pid)
+-              goto err_out_unlock;
++              goto err_out_free_nskb; 
++      /* netlink_unicast will either free the nskb or attach it to a socket */ 
+       status = netlink_unicast(ipqnl, nskb, peer_pid, MSG_DONTWAIT);
+       if (status < 0)
+               goto err_out_unlock;
+@@ -312,6 +313,9 @@
+       write_unlock_bh(&queue_lock);
+       return status;
++
++err_out_free_nskb:
++      kfree_skb(nskb); 
+       
+ err_out_unlock:
+       write_unlock_bh(&queue_lock);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ip_tables.c linux-2.4.20/net/ipv4/netfilter/ip_tables.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ip_tables.c    Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ip_tables.c        Wed Sep 24 09:18:12 2003
+@@ -4,6 +4,10 @@
+  * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+  * Copyright (C) 2009-2002 Netfilter core team <coreteam@netfilter.org>
+  *
++ *  6 Mar 2002 Robert Olsson <robban@robtex.com>
++ * 17 Apr 2003 Chris  Wilson <chris@netservers.co.uk>
++ *     - mark_source_chains speedup for complex chains
++ *
+  * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
+  *    - increase module usage count as soon as we have rules inside
+  *      a table
+@@ -24,6 +28,17 @@
+ #include <linux/proc_fs.h>
+ #include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_logging.h>
++
++#ifdef CONFIG_IP_NF_TARGET_TRACE_NEEDED
++static const char *hooknames[] = { 
++      [NF_IP_PRE_ROUTING] "PREROUTING",
++      [NF_IP_LOCAL_IN] "INPUT",
++      [NF_IP_FORWARD] "FORWARD",
++      [NF_IP_LOCAL_OUT] "OUTPUT",
++      [NF_IP_POST_ROUTING] "POSTROUTING",
++};
++#endif
+ /*#define DEBUG_IP_FIREWALL*/
+ /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
+@@ -68,13 +83,12 @@
+ #define inline
+ #endif
+-/* Locking is simple: we assume at worst case there will be one packet
+-   in user context and one from bottom halves (or soft irq if Alexey's
+-   softnet patch was applied).
+-
++/*
+    We keep a set of rules for each CPU, so we can avoid write-locking
+-   them; doing a readlock_bh() stops packets coming through if we're
+-   in user context.
++   them in the softirq when updating the counters and therefore
++   only need to read-lock in the softirq; doing a write_lock_bh() in user
++   context stops packets coming through and allows user context to read
++   the counters or update the rules.
+    To be cache friendly on SMP, we arrange them like so:
+    [ n-entries ]
+@@ -251,6 +265,39 @@
+       return (struct ipt_entry *)(base + offset);
+ }
++static inline int
++get_chainname_rulenum(struct ipt_entry *s, struct ipt_entry *e,
++                    char **chainname, u_int16_t *rulenum)
++{
++      struct ipt_entry_target *t;
++
++      (*rulenum)++;
++
++      if (s == e)
++              return 1;
++
++      t = ipt_get_target(s);
++      if (strcmp(t->u.kernel.target->name, IPT_ERROR_TARGET) == 0) {
++              *chainname = t->data;
++              (*rulenum) = 0;
++      }
++      
++      return 0;
++}
++
++/* All zeroes == unconditional rule. */
++static inline int
++unconditional(const struct ipt_ip *ip)
++{
++      unsigned int i;
++
++      for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++)
++              if (((__u32 *)ip)[i])
++                      return 0;
++
++      return 1;
++}
++
+ /* Returns one of the generic firewall policies, like NF_ACCEPT. */
+ unsigned int
+ ipt_do_table(struct sk_buff **pskb,
+@@ -326,6 +373,27 @@
+                       t = ipt_get_target(e);
+                       IP_NF_ASSERT(t->u.kernel.target);
++#ifdef CONFIG_IP_NF_TARGET_TRACE_NEEDED
++                      /* The packet traced and the rule isn't an unconditional return/END. */
++                      if (((*pskb)->nfcache & NFC_TRACE)
++                          && !(e->target_offset == sizeof(struct ipt_entry)
++                               && (strcmp(t->u.kernel.target->name,
++                                     IPT_STANDARD_TARGET) == 0)
++                               && !t->u.kernel.target->target
++                               && ((struct ipt_standard_target *)t)->verdict < 0
++                               && unconditional(&e->ip))) {
++                              char *chainname = (char *) hooknames[hook];
++                              u_int16_t rulenum = 0;
++                              
++                              IPT_ENTRY_ITERATE(get_entry(table_base, table->private->hook_entry[hook]),
++                                                table->private->size,
++                                                get_chainname_rulenum,
++                                                e, &chainname, &rulenum);
++                                                
++                              nf_log_ip_packet(pskb, hook, in, out, "TRACE: %s/%s/%u ",
++                                               table->name, chainname, rulenum);
++                      }
++#endif
+                       /* Standard target? */
+                       if (!t->u.kernel.target->target) {
+                               int v;
+@@ -482,19 +550,6 @@
+       return find_inlist_lock(&ipt_target, name, "ipt_", error, mutex);
+ }
+-/* All zeroes == unconditional rule. */
+-static inline int
+-unconditional(const struct ipt_ip *ip)
+-{
+-      unsigned int i;
+-
+-      for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++)
+-              if (((__u32 *)ip)[i])
+-                      return 0;
+-
+-      return 1;
+-}
+-
+ /* Figures out from what hook each rule can be called: returns 0 if
+    there are loops.  Puts hook bitmask in comefrom. */
+ static int
+@@ -502,6 +557,9 @@
+ {
+       unsigned int hook;
++      /* keep track of where we have been: */
++      unsigned char *been = vmalloc(newinfo->size);
++
+       /* No recursion; use packet counter to save back ptrs (reset
+          to 0 as we leave), and comefrom to save source hook bitmask */
+       for (hook = 0; hook < NF_IP_NUMHOOKS; hook++) {
+@@ -514,6 +572,7 @@
+               /* Set initial back pointer. */
+               e->counters.pcnt = pos;
++              memset(been, 0, newinfo->size);
+               for (;;) {
+                       struct ipt_standard_target *t
+@@ -522,6 +581,7 @@
+                       if (e->comefrom & (1 << NF_IP_NUMHOOKS)) {
+                               printk("iptables: loop hook %u pos %u %08X.\n",
+                                      hook, pos, e->comefrom);
++                              vfree(been);
+                               return 0;
+                       }
+                       e->comefrom
+@@ -569,10 +629,14 @@
+                       } else {
+                               int newpos = t->verdict;
+-                              if (strcmp(t->target.u.user.name,
++                              if ( (pos < 0 || pos >= newinfo->size
++                                    || !been[pos]) 
++                                  && strcmp(t->target.u.user.name,
+                                          IPT_STANDARD_TARGET) == 0
+                                   && newpos >= 0) {
+                                       /* This a jump; chase it. */
++                                      if (pos >= 0 && pos < newinfo->size)
++                                              been[pos]++;
+                                       duprintf("Jump rule %u -> %u\n",
+                                                pos, newpos);
+                               } else {
+@@ -588,6 +652,7 @@
+               next:
+               duprintf("Finished chain %u\n", hook);
+       }
++      vfree(been);
+       return 1;
+ }
+@@ -1630,7 +1695,7 @@
+                    u_int8_t type, u_int8_t code,
+                    int invert)
+ {
+-      return (type == test_type && code >= min_code && code <= max_code)
++      return ((test_type == 0xFF) || (type == test_type && code >= min_code && code <= max_code))
+               ^ invert;
+ }
+@@ -1699,14 +1764,15 @@
+ = { { NULL, NULL }, "icmp", &icmp_match, &icmp_checkentry, NULL };
+ #ifdef CONFIG_PROC_FS
+-static inline int print_name(const struct ipt_table *t,
++static inline int print_name(const char *i,
+                            off_t start_offset, char *buffer, int length,
+                            off_t *pos, unsigned int *count)
+ {
+       if ((*count)++ >= start_offset) {
+               unsigned int namelen;
+-              namelen = sprintf(buffer + *pos, "%s\n", t->name);
++              namelen = sprintf(buffer + *pos, "%s\n",
++                                i + sizeof(struct list_head));
+               if (*pos + namelen > length) {
+                       /* Stop iterating */
+                       return 1;
+@@ -1724,7 +1790,7 @@
+       if (down_interruptible(&ipt_mutex) != 0)
+               return 0;
+-      LIST_FIND(&ipt_tables, print_name, struct ipt_table *,
++      LIST_FIND(&ipt_tables, print_name, void *,
+                 offset, buffer, length, &pos, &count);
+       up(&ipt_mutex);
+@@ -1733,6 +1799,46 @@
+       *start=(char *)((unsigned long)count-offset);
+       return pos;
+ }
++
++static int ipt_get_targets(char *buffer, char **start, off_t offset, int length)
++{
++      off_t pos = 0;
++      unsigned int count = 0;
++
++      if (down_interruptible(&ipt_mutex) != 0)
++              return 0;
++
++      LIST_FIND(&ipt_target, print_name, void *,
++                offset, buffer, length, &pos, &count);
++      
++      up(&ipt_mutex);
++
++      *start = (char *)((unsigned long)count - offset);
++      return pos;
++}
++
++static int ipt_get_matches(char *buffer, char **start, off_t offset, int length)
++{
++      off_t pos = 0;
++      unsigned int count = 0;
++
++      if (down_interruptible(&ipt_mutex) != 0)
++              return 0;
++      
++      LIST_FIND(&ipt_match, print_name, void *,
++                offset, buffer, length, &pos, &count);
++
++      up(&ipt_mutex);
++
++      *start = (char *)((unsigned long)count - offset);
++      return pos;
++}
++
++static struct { char *name; get_info_t *get_info; } ipt_proc_entry[] =
++{ { "ip_tables_names", ipt_get_tables },
++  { "ip_tables_targets", ipt_get_targets },
++  { "ip_tables_matches", ipt_get_matches },
++  { NULL, NULL} };
+ #endif /*CONFIG_PROC_FS*/
+ static int __init init(void)
+@@ -1758,13 +1864,19 @@
+ #ifdef CONFIG_PROC_FS
+       {
+       struct proc_dir_entry *proc;
++      int i;
+-      proc = proc_net_create("ip_tables_names", 0, ipt_get_tables);
+-      if (!proc) {
+-              nf_unregister_sockopt(&ipt_sockopts);
+-              return -ENOMEM;
++      for (i = 0; ipt_proc_entry[i].name; i++) {
++              proc = proc_net_create(ipt_proc_entry[i].name, 0,
++                                     ipt_proc_entry[i].get_info);
++              if (!proc) {
++                      while (--i >= 0)
++                              proc_net_remove(ipt_proc_entry[i].name);
++                      nf_unregister_sockopt(&ipt_sockopts);
++                      return -ENOMEM;
++              }
++              proc->owner = THIS_MODULE;
+       }
+-      proc->owner = THIS_MODULE;
+       }
+ #endif
+@@ -1776,7 +1888,11 @@
+ {
+       nf_unregister_sockopt(&ipt_sockopts);
+ #ifdef CONFIG_PROC_FS
+-      proc_net_remove("ip_tables_names");
++      {
++      int i;
++      for (i = 0; ipt_proc_entry[i].name; i++)
++              proc_net_remove(ipt_proc_entry[i].name);
++      }
+ #endif
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipchains_core.c linux-2.4.20/net/ipv4/netfilter/ipchains_core.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipchains_core.c        Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ipchains_core.c    Wed Sep 24 09:16:14 2003
+@@ -1566,16 +1566,8 @@
+ /* File offset is actually in records, not bytes. */
+ static int ip_chain_procinfo(char *buffer, char **start,
+-                           off_t offset, int length
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+-                           , int reset
+-#endif
+-      )
++                           off_t offset, int length)
+ {
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+-      /* FIXME: No more `atomic' read and reset.  Wonderful 8-( --RR */
+-      int reset = 0;
+-#endif
+       struct ip_chain *i;
+       struct ip_fwkernel *j = ip_fw_chains->chain;
+       unsigned long flags;
+@@ -1612,9 +1604,6 @@
+                               len = last_len;
+                               goto outside;
+                       }
+-                      else if (reset)
+-                              memset(j->counters, 0,
+-                                     sizeof(struct ip_counters)*NUM_SLOTS);
+               }
+       }
+ outside:
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipfwadm_core.c linux-2.4.20/net/ipv4/netfilter/ipfwadm_core.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipfwadm_core.c Wed Sep 24 08:52:55 2003
++++ linux-2.4.20/net/ipv4/netfilter/ipfwadm_core.c     Wed Sep 24 09:16:14 2003
+@@ -1108,9 +1108,8 @@
+ #endif /* CONFIG_IP_FIREWALL */
+ #if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
+-
+ static int ip_chain_procinfo(int stage, char *buffer, char **start,
+-                           off_t offset, int length, int reset)
++                           off_t offset, int length)
+ {
+       off_t pos=0, begin=0;
+       struct ip_fw *i;
+@@ -1181,12 +1180,6 @@
+                       len = last_len;
+                       break;
+               }
+-              else if(reset)
+-              {
+-                      /* This needs to be done at this specific place! */
+-                      i->fw_pcnt=0L;
+-                      i->fw_bcnt=0L;
+-              }
+               last_len = len;
+               i=i->fw_next;
+       }
+@@ -1200,69 +1193,30 @@
+ #endif
+ #ifdef CONFIG_IP_ACCT
+-
+ static int ip_acct_procinfo(char *buffer, char **start, off_t offset,
+-                          int length
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+-                          , int reset
+-#endif
+-      )
++                          int length)
+ {
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+-      /* FIXME: No more `atomic' read and reset.  Wonderful 8-( --RR */
+-      int reset = 0;
+-#endif
+-      return ip_chain_procinfo(IP_FW_ACCT, buffer,start, offset,length,
+-                               reset);
++      return ip_chain_procinfo(IP_FW_ACCT, buffer,start, offset,length);
+ }
+-
+ #endif
+ #ifdef CONFIG_IP_FIREWALL
+-
+ static int ip_fw_in_procinfo(char *buffer, char **start, off_t offset,
+-                            int length
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+-                           , int reset
+-#endif
+-      )
++                            int length)
+ {
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+-      /* FIXME: No more `atomic' read and reset.  Wonderful 8-( --RR */
+-      int reset = 0;
+-#endif
+-      return ip_chain_procinfo(IP_FW_IN, buffer,start,offset,length,
+-                               reset);
++      return ip_chain_procinfo(IP_FW_IN, buffer,start,offset,length);
+ }
+ static int ip_fw_out_procinfo(char *buffer, char **start, off_t offset,
+-                            int length
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+-                          , int reset
+-#endif
+-      )
++                            int length)
+ {
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+-      /* FIXME: No more `atomic' read and reset.  Wonderful 8-( --RR */
+-      int reset = 0;
+-#endif
+-      return ip_chain_procinfo(IP_FW_OUT, buffer,start,offset,length,
+-                               reset);
++      return ip_chain_procinfo(IP_FW_OUT, buffer,start,offset,length);
+ }
+ static int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset,
+-                            int length
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+-                          , int reset
+-#endif
+-      )
++                            int length)
+ {
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+-      /* FIXME: No more `atomic' read and reset.  Wonderful 8-( --RR */
+-      int reset = 0;
+-#endif
+-      return ip_chain_procinfo(IP_FW_FWD, buffer,start,offset,length,
+-                               reset);
++      return ip_chain_procinfo(IP_FW_FWD, buffer,start,offset,length);
+ }
+ #endif
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_CLASSIFY.c linux-2.4.20/net/ipv4/netfilter/ipt_CLASSIFY.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_CLASSIFY.c Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_CLASSIFY.c     Wed Sep 24 09:17:14 2003
+@@ -0,0 +1,82 @@
++/*
++ * This is a module which is used for setting the skb->priority field
++ * of an skb for qdisc classification.
++ */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_CLASSIFY.h>
++
++MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("iptables qdisc classification target module");
++
++static unsigned int
++target(struct sk_buff **pskb,
++       unsigned int hooknum,
++       const struct net_device *in,
++       const struct net_device *out,
++       const void *targinfo,
++       void *userinfo)
++{
++      const struct ipt_classify_target_info *clinfo = targinfo;
++
++      if((*pskb)->priority != clinfo->priority) {
++              (*pskb)->priority = clinfo->priority;
++              (*pskb)->nfcache |= NFC_ALTERED;
++      }
++
++      return IPT_CONTINUE;
++}
++
++static int
++checkentry(const char *tablename,
++           const struct ipt_entry *e,
++           void *targinfo,
++           unsigned int targinfosize,
++           unsigned int hook_mask)
++{
++      if (targinfosize != IPT_ALIGN(sizeof(struct ipt_classify_target_info))){
++              printk(KERN_ERR "CLASSIFY: invalid size (%u != %u).\n",
++                     targinfosize,
++                     IPT_ALIGN(sizeof(struct ipt_classify_target_info)));
++              return 0;
++      }
++      
++      if (hook_mask & ~(1 << NF_IP_POST_ROUTING)) {
++              printk(KERN_ERR "CLASSIFY: only valid in POST_ROUTING.\n");
++              return 0;
++      }
++
++      if (strcmp(tablename, "mangle") != 0) {
++              printk(KERN_WARNING "CLASSIFY: can only be called from "
++                                  "\"mangle\" table, not \"%s\".\n",
++                                  tablename);
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ipt_target ipt_classify_reg
++= { { NULL, NULL }, "CLASSIFY", target, checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ipt_register_target(&ipt_classify_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&ipt_classify_reg);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_CONNMARK.c linux-2.4.20/net/ipv4/netfilter/ipt_CONNMARK.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_CONNMARK.c Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_CONNMARK.c     Wed Sep 24 09:17:17 2003
+@@ -0,0 +1,87 @@
++/* This is a module which is used for setting/remembering the mark field of
++ * an connection, or optionally restore it to the skb
++ */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_CONNMARK.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++
++static unsigned int
++target(struct sk_buff **pskb,
++       unsigned int hooknum,
++       const struct net_device *in,
++       const struct net_device *out,
++       const void *targinfo,
++       void *userinfo)
++{
++      const struct ipt_connmark_target_info *markinfo = targinfo;
++
++      enum ip_conntrack_info ctinfo;
++      struct ip_conntrack *ct = ip_conntrack_get((*pskb), &ctinfo);
++      if (ct) {
++          switch(markinfo->mode) {
++          case IPT_CONNMARK_SET:
++              ct->mark = markinfo->mark;
++              break;
++          case IPT_CONNMARK_SAVE:
++              ct->mark = (*pskb)->nfmark;
++              break;
++          case IPT_CONNMARK_RESTORE:
++              if (ct->mark != (*pskb)->nfmark) {
++                  (*pskb)->nfmark = ct->mark;
++                  (*pskb)->nfcache |= NFC_ALTERED;
++              }
++              break;
++          }
++      }
++
++      return IPT_CONTINUE;
++}
++
++static int
++checkentry(const char *tablename,
++         const struct ipt_entry *e,
++           void *targinfo,
++           unsigned int targinfosize,
++           unsigned int hook_mask)
++{
++      struct ipt_connmark_target_info *matchinfo = targinfo;
++      if (targinfosize != IPT_ALIGN(sizeof(struct ipt_connmark_target_info))) {
++              printk(KERN_WARNING "CONNMARK: targinfosize %u != %Zu\n",
++                     targinfosize,
++                     IPT_ALIGN(sizeof(struct ipt_connmark_target_info)));
++              return 0;
++      }
++
++      if (matchinfo->mode == IPT_CONNMARK_RESTORE) {
++          if (strcmp(tablename, "mangle") != 0) {
++                  printk(KERN_WARNING "CONNMARK: restore can only be called from \"mangle\" table, not \"%s\"\n", tablename);
++                  return 0;
++          }
++      }
++
++      return 1;
++}
++
++static struct ipt_target ipt_connmark_reg
++= { { NULL, NULL }, "CONNMARK", target, checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ipt_register_target(&ipt_connmark_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&ipt_connmark_reg);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_ECN.c linux-2.4.20/net/ipv4/netfilter/ipt_ECN.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_ECN.c      Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ipt_ECN.c  Wed Sep 24 09:16:14 2003
+@@ -87,8 +87,8 @@
+       }
+       
+       if (diffs[0] != *tcpflags) {
+-              diffs[0] = htons(diffs[0]) ^ 0xFFFF;
+-              diffs[1] = htons(*tcpflags);
++              diffs[0] = diffs[0] ^ 0xFFFF;
++              diffs[1] = *tcpflags;
+               tcph->check = csum_fold(csum_partial((char *)diffs,
+                                                   sizeof(diffs),
+                                                   tcph->check^0xFFFF));
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_IMQ.c linux-2.4.20/net/ipv4/netfilter/ipt_IMQ.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_IMQ.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_IMQ.c  Wed Sep 24 09:17:19 2003
+@@ -0,0 +1,78 @@
++/* This target marks packets to be enqueued to an imq device */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_IMQ.h>
++#include <linux/imq.h>
++
++static unsigned int imq_target(struct sk_buff **pskb,
++                             unsigned int hooknum,
++                             const struct net_device *in,
++                             const struct net_device *out,
++                             const void *targinfo,
++                             void *userinfo)
++{
++      struct ipt_imq_info *mr = (struct ipt_imq_info*)targinfo;
++
++      (*pskb)->imq_flags = mr->todev | IMQ_F_ENQUEUE;
++      (*pskb)->nfcache |= NFC_ALTERED;
++
++      return IPT_CONTINUE;
++}
++
++static int imq_checkentry(const char *tablename,
++                        const struct ipt_entry *e,
++                        void *targinfo,
++                        unsigned int targinfosize,
++                        unsigned int hook_mask)
++{
++      struct ipt_imq_info *mr;
++
++      if (targinfosize != IPT_ALIGN(sizeof(struct ipt_imq_info))) {
++              printk(KERN_WARNING "IMQ: invalid targinfosize\n");
++              return 0;
++      }
++      mr = (struct ipt_imq_info*)targinfo;
++
++      if (strcmp(tablename, "mangle") != 0) {
++              printk(KERN_WARNING
++                     "IMQ: IMQ can only be called from \"mangle\" table, not \"%s\"\n",
++                     tablename);
++              return 0;
++      }
++      
++      if (mr->todev > IMQ_MAX_DEVS) {
++              printk(KERN_WARNING
++                     "IMQ: invalid device specified, highest is %u\n",
++                     IMQ_MAX_DEVS);
++              return 0;
++      }
++      
++      return 1;
++}
++
++static struct ipt_target ipt_imq_reg = {
++      { NULL, NULL},
++      "IMQ",
++      imq_target,
++      imq_checkentry,
++      NULL,
++      THIS_MODULE
++};
++
++static int __init init(void)
++{
++      if (ipt_register_target(&ipt_imq_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&ipt_imq_reg);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_IPMARK.c linux-2.4.20/net/ipv4/netfilter/ipt_IPMARK.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_IPMARK.c   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_IPMARK.c       Wed Sep 24 09:17:23 2003
+@@ -0,0 +1,88 @@
++/* This is a module which is used for setting the NFMARK field of an skb. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_IPMARK.h>
++
++MODULE_AUTHOR("Grzegorz Janoszka <Grzegorz.Janoszka@pro.onet.pl>");
++MODULE_DESCRIPTION("IP tables IPMARK: mark based on ip address");
++MODULE_LICENSE("GPL");
++
++static unsigned int
++target(struct sk_buff **pskb,
++       unsigned int hooknum,
++       const struct net_device *in,
++       const struct net_device *out,
++       const void *targinfo,
++       void *userinfo)
++{
++      const struct ipt_ipmark_target_info *ipmarkinfo = targinfo;
++
++      struct iphdr *iph = (*pskb)->nh.iph;
++
++      unsigned long mark;
++      char *a, t;
++
++      if(ipmarkinfo->addr == IPT_IPMARK_SRC)
++        mark = (unsigned long) iph->saddr;
++      else
++        mark = (unsigned long) iph->daddr;
++
++// mark has ip address in little indian, we have to change the byte order:
++      a = (char *) &mark;
++      t = *a; *a=*(a+3); *(a+3)=t;
++      t=*(a+1); *(a+1)=*(a+2); *(a+2)=t;
++
++      mark &= ipmarkinfo->andmask;
++      mark |= ipmarkinfo->ormask;
++      
++      if((*pskb)->nfmark != mark) {
++              (*pskb)->nfmark = mark;
++              (*pskb)->nfcache |= NFC_ALTERED;
++      }
++      return IPT_CONTINUE;
++}
++
++static int
++checkentry(const char *tablename,
++         const struct ipt_entry *e,
++           void *targinfo,
++           unsigned int targinfosize,
++           unsigned int hook_mask)
++{
++      if (targinfosize != IPT_ALIGN(sizeof(struct ipt_ipmark_target_info))) {
++              printk(KERN_WARNING "IPMARK: targinfosize %u != %Zu\n",
++                     targinfosize,
++                     IPT_ALIGN(sizeof(struct ipt_ipmark_target_info)));
++              return 0;
++      }
++
++      if (strcmp(tablename, "mangle") != 0) {
++              printk(KERN_WARNING "IPMARK: can only be called from \"mangle\" table, not \"%s\"\n", tablename);
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ipt_target ipt_ipmark_reg
++= { { NULL, NULL }, "IPMARK", target, checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ipt_register_target(&ipt_ipmark_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&ipt_ipmark_reg);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c linux-2.4.20/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c        Wed Sep 24 09:16:30 2003
+@@ -0,0 +1,84 @@
++/**
++ * Strip all IP options in the IP packet header.
++ *
++ * (C) 2001 by Fabrice MARIE <fabrice@netfilter.org>
++ * This software is distributed under GNU GPL v2, 1991
++ */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++MODULE_AUTHOR("Fabrice MARIE <fabrice@netfilter.org>");
++MODULE_DESCRIPTION("Strip all options in IPv4 packets");
++MODULE_LICENSE("GPL");
++
++static unsigned int
++target(struct sk_buff **pskb,
++       unsigned int hooknum,
++       const struct net_device *in,
++       const struct net_device *out,
++       const void *targinfo,
++       void *userinfo)
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct sk_buff *skb = (*pskb);
++      struct ip_options * opt;
++      unsigned char * optiph = skb->nh.raw;
++      int l = ((struct ip_options *)(&(IPCB(skb)->opt)))->optlen;
++      
++
++      /* if no options in packet then nothing to clear. */
++      if (iph->ihl * 4 == sizeof(struct iphdr))
++              return IPT_CONTINUE;
++
++      /* else clear all options */
++      memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
++      memset(optiph+sizeof(struct iphdr), IPOPT_NOOP, l);
++      opt = &(IPCB(skb)->opt);
++      opt->is_data = 0;
++      opt->optlen = l;
++
++      skb->nfcache |= NFC_ALTERED;
++
++        return IPT_CONTINUE;
++}
++
++static int
++checkentry(const char *tablename,
++         const struct ipt_entry *e,
++           void *targinfo,
++           unsigned int targinfosize,
++           unsigned int hook_mask)
++{
++      if (strcmp(tablename, "mangle")) {
++              printk(KERN_WARNING "IPV4OPTSSTRIP: can only be called from \"mangle\" table, not \"%s\"\n", tablename);
++              return 0;
++      }
++      /* nothing else to check because no parameters */
++      return 1;
++}
++
++static struct ipt_target ipt_ipv4optsstrip_reg
++= { { NULL, NULL }, "IPV4OPTSSTRIP", target, checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ipt_register_target(&ipt_ipv4optsstrip_reg))
++              return -EINVAL;
++      printk("ipt_IPV4OPTSSTRIP loaded\n");
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&ipt_ipv4optsstrip_reg);
++      printk("ipt_IPV4OPTSSTRIP unloaded\n");
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_LOG.c linux-2.4.20/net/ipv4/netfilter/ipt_LOG.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_LOG.c      Mon Feb 25 19:38:14 2002
++++ linux-2.4.20/net/ipv4/netfilter/ipt_LOG.c  Wed Sep 24 09:16:23 2003
+@@ -3,16 +3,20 @@
+  */
+ #include <linux/module.h>
+ #include <linux/skbuff.h>
+-#include <linux/ip.h>
+ #include <linux/spinlock.h>
++#include <linux/ip.h>
+ #include <net/icmp.h>
+ #include <net/udp.h>
+ #include <net/tcp.h>
+-#include <linux/netfilter_ipv4/ip_tables.h>
+-
+-struct in_device;
+ #include <net/route.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
+ #include <linux/netfilter_ipv4/ipt_LOG.h>
++#include <linux/netfilter_ipv4/ip_logging.h>
++
++static unsigned int nflog = 1;
++MODULE_PARM(nflog, "i");
++MODULE_PARM_DESC(nflog, "register as internal netfilter logging module");
+ #if 0
+ #define DEBUGP printk
+@@ -20,10 +24,20 @@
+ #define DEBUGP(format, args...)
+ #endif
++/* FIXME: move to ip.h like in 2.5 */
++struct ahhdr {
++      __u8    nexthdr;
++      __u8    hdrlen;
++      __u16   reserved;
++      __u32   spi;
++      __u32   seq_no;
++};
++
+ struct esphdr {
+       __u32   spi;
+-}; /* FIXME evil kludge */
+-        
++      __u32   seq_no;
++};
++
+ /* Use lock to serialize, so printks don't overlap */
+ static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
+@@ -58,7 +72,8 @@
+               printk("FRAG:%u ", ntohs(iph->frag_off) & IP_OFFSET);
+       if ((info->logflags & IPT_LOG_IPOPT)
+-          && iph->ihl * 4 != sizeof(struct iphdr)) {
++          && iph->ihl * 4 != sizeof(struct iphdr)
++          && iph->ihl * 4 >= datalen) {
+               unsigned int i;
+               /* Max length: 127 "OPT (" 15*4*2chars ") " */
+@@ -230,13 +245,30 @@
+               break;
+       }
+       /* Max Length */
+-      case IPPROTO_AH:
++      case IPPROTO_AH: {
++              struct ahhdr *ah = protoh;
++
++              /* Max length: 9 "PROTO=AH " */
++              printk("PROTO=AH ");
++
++              if (ntohs(iph->frag_off) & IP_OFFSET)
++                      break;
++
++              /* Max length: 25 "INCOMPLETE [65535 bytes] " */
++              if (datalen < sizeof (*ah)) {
++                      printk("INCOMPLETE [%u bytes] ", datalen);
++                      break;
++              }
++
++              /* Length: 15 "SPI=0xF1234567 " */
++              printk("SPI=0x%x ", ntohl(ah->spi) );
++              break;
++      }
+       case IPPROTO_ESP: {
+               struct esphdr *esph = protoh;
+-              int esp= (iph->protocol==IPPROTO_ESP);
+               /* Max length: 10 "PROTO=ESP " */
+-              printk("PROTO=%s ",esp? "ESP" : "AH");
++              printk("PROTO=ESP ");
+               if (ntohs(iph->frag_off) & IP_OFFSET)
+                       break;
+@@ -270,23 +302,21 @@
+       /* maxlen = 230+   91  + 230 + 252 = 803 */
+ }
+-static unsigned int
+-ipt_log_target(struct sk_buff **pskb,
++static void
++ipt_log_packet(struct sk_buff **pskb,
+              unsigned int hooknum,
+              const struct net_device *in,
+              const struct net_device *out,
+-             const void *targinfo,
+-             void *userinfo)
++             const struct ipt_log_info *loginfo,
++             const char *level_string,
++             const char *prefix)
+ {
+       struct iphdr *iph = (*pskb)->nh.iph;
+-      const struct ipt_log_info *loginfo = targinfo;
+-      char level_string[4] = "< >";
+-      level_string[1] = '0' + (loginfo->level % 8);
+       spin_lock_bh(&log_lock);
+       printk(level_string);
+       printk("%sIN=%s OUT=%s ",
+-             loginfo->prefix,
++             prefix == NULL ? loginfo->prefix : prefix,
+              in ? in->name : "",
+              out ? out->name : "");
+       if (in && !out) {
+@@ -306,10 +336,59 @@
+       dump_packet(loginfo, iph, (*pskb)->len, 1);
+       printk("\n");
+       spin_unlock_bh(&log_lock);
++}
++
++static unsigned int
++ipt_log_target(struct sk_buff **pskb,
++             unsigned int hooknum,
++             const struct net_device *in,
++             const struct net_device *out,
++             const void *targinfo,
++             void *userinfo)
++{
++      const struct ipt_log_info *loginfo = targinfo;
++      char level_string[4] = "< >";
++
++      level_string[1] = '0' + (loginfo->level % 8);
++      ipt_log_packet(pskb, hooknum, in, out, loginfo, level_string, NULL);
+       return IPT_CONTINUE;
+ }
++static void
++ip_log_packet_fn(struct sk_buff **pskb,
++               unsigned int hooknum,
++               const struct net_device *in,
++               const struct net_device *out,
++               const char *prefix)
++{
++      struct ipt_log_info loginfo = { 
++              .level = 0, 
++              .logflags = IPT_LOG_MASK, 
++              .prefix = "" 
++      };
++
++      ipt_log_packet(pskb, hooknum, in, out, &loginfo, KERN_WARNING, prefix);
++}
++
++static void
++ip_log_fn(char *pfh, size_t len,
++        const char *prefix)
++{
++      struct iphdr *iph = (struct iphdr *)pfh;
++      struct ipt_log_info loginfo = { 
++              .level = 0, 
++              .logflags = IPT_LOG_MASK, 
++              .prefix = "",
++      };
++
++      spin_lock_bh(&log_lock);
++      printk(KERN_WARNING "%s", prefix);
++      dump_packet(&loginfo, iph, len, 1);
++      printk("\n");
++      spin_unlock_bh(&log_lock);
++}
++
+ static int ipt_log_checkentry(const char *tablename,
+                             const struct ipt_entry *e,
+                             void *targinfo,
+@@ -341,17 +420,23 @@
+ static struct ipt_target ipt_log_reg
+ = { { NULL, NULL }, "LOG", ipt_log_target, ipt_log_checkentry, NULL, 
+     THIS_MODULE };
++static struct nf_logging_t ip_logging_fn
++= { ip_log_packet_fn, ip_log_fn };
+ static int __init init(void)
+ {
+       if (ipt_register_target(&ipt_log_reg))
+               return -EINVAL;
+-
++      if (nflog)
++              nf_ip_log_register(&ip_logging_fn);
++      
+       return 0;
+ }
+ static void __exit fini(void)
+ {
++      if (nflog)
++              nf_ip_log_unregister(&ip_logging_fn);
+       ipt_unregister_target(&ipt_log_reg);
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_MASQUERADE.c linux-2.4.20/net/ipv4/netfilter/ipt_MASQUERADE.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_MASQUERADE.c       Sun Sep 30 19:26:08 2001
++++ linux-2.4.20/net/ipv4/netfilter/ipt_MASQUERADE.c   Wed Sep 24 09:16:25 2003
+@@ -87,13 +87,21 @@
+       key.dst = (*pskb)->nh.iph->daddr;
+       key.src = 0; /* Unknown: that's what we're trying to establish */
+       key.tos = RT_TOS((*pskb)->nh.iph->tos)|RTO_CONN;
+-      key.oif = out->ifindex;
++      key.oif = 0;
+ #ifdef CONFIG_IP_ROUTE_FWMARK
+       key.fwmark = (*pskb)->nfmark;
+ #endif
+       if (ip_route_output_key(&rt, &key) != 0) {
+-              /* Shouldn't happen */
+-              printk("MASQUERADE: No route: Rusty's brain broke!\n");
++                /* Funky routing can do this. */
++                if (net_ratelimit())
++                        printk("MASQUERADE:"
++                               " No route: Rusty's brain broke!\n");
++                return NF_DROP;
++        }
++        if (rt->u.dst.dev != out) {
++                if (net_ratelimit())
++                        printk("MASQUERADE:"
++                               " Route sent us somewhere else.\n");
+               return NF_DROP;
+       }
+@@ -116,63 +124,37 @@
+ }
+ static inline int
+-device_cmp(const struct ip_conntrack *i, void *ifindex)
++device_cmp(const struct ip_conntrack *i, void *_ina)
+ {
+-      int ret;
++      int ret = 0;
++      struct in_ifaddr *ina = _ina;
+       READ_LOCK(&masq_lock);
+-      ret = (i->nat.masq_index == (int)(long)ifindex);
++      /* If it's masquerading out this interface with a different address,
++       * or we don't know the new address of this interface. */
++      if (i->nat.masq_index == ina->ifa_dev->dev->ifindex
++          && i->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip != ina->ifa_address)
++              ret = 1;
+       READ_UNLOCK(&masq_lock);
+       return ret;
+ }
+-static int masq_device_event(struct notifier_block *this,
+-                           unsigned long event,
+-                           void *ptr)
+-{
+-      struct net_device *dev = ptr;
+-
+-      if (event == NETDEV_DOWN) {
+-              /* Device was downed.  Search entire table for
+-                 conntracks which were associated with that device,
+-                 and forget them. */
+-              IP_NF_ASSERT(dev->ifindex != 0);
+-
+-              ip_ct_selective_cleanup(device_cmp, (void *)(long)dev->ifindex);
+-      }
+-
+-      return NOTIFY_DONE;
+-}
+-
+ static int masq_inet_event(struct notifier_block *this,
+                          unsigned long event,
+                          void *ptr)
+ {
+-      struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev;
+-
+-      if (event == NETDEV_DOWN) {
+-              /* IP address was deleted.  Search entire table for
+-                 conntracks which were associated with that device,
+-                 and forget them. */
+-              IP_NF_ASSERT(dev->ifindex != 0);
+-
+-              ip_ct_selective_cleanup(device_cmp, (void *)(long)dev->ifindex);
+-      }
++      /* For some configurations, interfaces often come back with
++       * the same address.  If not, clean up old conntrack
++       * entries. */
++      if (event == NETDEV_UP)
++              ip_ct_selective_cleanup(device_cmp, ptr);
+       return NOTIFY_DONE;
+ }
+-static struct notifier_block masq_dev_notifier = {
+-      masq_device_event,
+-      NULL,
+-      0
+-};
+-
+ static struct notifier_block masq_inet_notifier = {
+-      masq_inet_event,
+-      NULL,
+-      0
++      .notifier_call = masq_inet_event
+ };
+ static struct ipt_target masquerade
+@@ -185,12 +167,9 @@
+       ret = ipt_register_target(&masquerade);
+-      if (ret == 0) {
+-              /* Register for device down reports */
+-              register_netdevice_notifier(&masq_dev_notifier);
++      if (ret == 0)
+               /* Register IP address change reports */
+               register_inetaddr_notifier(&masq_inet_notifier);
+-      }
+       return ret;
+ }
+@@ -198,7 +177,6 @@
+ static void __exit fini(void)
+ {
+       ipt_unregister_target(&masquerade);
+-      unregister_netdevice_notifier(&masq_dev_notifier);
+       unregister_inetaddr_notifier(&masq_inet_notifier);      
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_MIRROR.c linux-2.4.20/net/ipv4/netfilter/ipt_MIRROR.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_MIRROR.c   Fri Dec 21 17:42:05 2001
++++ linux-2.4.20/net/ipv4/netfilter/ipt_MIRROR.c       Wed Sep 24 09:16:17 2003
+@@ -32,7 +32,6 @@
+ #include <linux/netfilter_ipv4/ip_tables.h>
+ #include <linux/netdevice.h>
+ #include <linux/route.h>
+-struct in_device;
+ #include <net/route.h>
+ #if 0
+@@ -41,31 +40,47 @@
+ #define DEBUGP(format, args...)
+ #endif
+-static int route_mirror(struct sk_buff *skb)
++static inline struct rtable *route_mirror(struct sk_buff *skb, int local)
+ {
+         struct iphdr *iph = skb->nh.iph;
++      struct dst_entry *odst;
++      struct rt_key key = {};
+       struct rtable *rt;
+-      /* Backwards */
+-      if (ip_route_output(&rt, iph->saddr, iph->daddr,
+-                          RT_TOS(iph->tos) | RTO_CONN,
+-                          0)) {
+-              return 0;
++      if (local) {
++              key.dst = iph->saddr;
++              key.src = iph->daddr;
++              key.tos = RT_TOS(iph->tos);
++
++              if (ip_route_output_key(&rt, &key) != 0)
++                      return NULL;
++      } else {
++              /* non-local src, find valid iif to satisfy
++               * rp-filter when calling ip_route_input. */
++              key.dst = iph->daddr;
++              if (ip_route_output_key(&rt, &key) != 0)
++                      return NULL;
++
++              odst = skb->dst;
++              if (ip_route_input(skb, iph->saddr, iph->daddr,
++                                 RT_TOS(iph->tos), rt->u.dst.dev) != 0) {
++                      dst_release(&rt->u.dst);
++                      return NULL;
++              }
++              dst_release(&rt->u.dst);
++              rt = (struct rtable *)skb->dst;
++              skb->dst = odst;
+       }
+-      /* check if the interface we are leaving by is the same as the
+-           one we arrived on */
+-      if (skb->dev == rt->u.dst.dev) {
+-              /* Drop old route. */
+-              dst_release(skb->dst);
+-              skb->dst = &rt->u.dst;
+-              return 1;
++      if (rt->u.dst.error) {
++              dst_release(&rt->u.dst);
++              rt = NULL;
+       }
+-      return 0;
++
++      return rt;
+ }
+-static void
+-ip_rewrite(struct sk_buff *skb)
++static inline void ip_rewrite(struct sk_buff *skb)
+ {
+       struct iphdr *iph = skb->nh.iph;
+       u32 odaddr = iph->saddr;
+@@ -105,32 +120,48 @@
+                                     const void *targinfo,
+                                     void *userinfo)
+ {
+-      if (((*pskb)->dst != NULL) &&
+-          route_mirror(*pskb)) {
+-
+-              ip_rewrite(*pskb);
++      struct rtable *rt;
++      struct sk_buff *nskb;
++      unsigned int hh_len;
+-              /* If we are not at FORWARD hook (INPUT/PREROUTING),
+-               * the TTL isn't decreased by the IP stack */
+-              if (hooknum != NF_IP_FORWARD) {
+-                      struct iphdr *iph = (*pskb)->nh.iph;
+-                      if (iph->ttl <= 1) {
+-                              /* this will traverse normal stack, and 
+-                               * thus call conntrack on the icmp packet */
+-                              icmp_send(*pskb, ICMP_TIME_EXCEEDED, 
+-                                        ICMP_EXC_TTL, 0);
+-                              return NF_DROP;
+-                      }
+-                      ip_decrease_ttl(iph);
++      /* If we are not at FORWARD hook (INPUT/PREROUTING),
++       * the TTL isn't decreased by the IP stack */
++      if (hooknum != NF_IP_FORWARD) {
++              struct iphdr *iph = (*pskb)->nh.iph;
++              if (iph->ttl <= 1) {
++                      /* this will traverse normal stack, and 
++                       * thus call conntrack on the icmp packet */
++                      icmp_send(*pskb, ICMP_TIME_EXCEEDED, 
++                                ICMP_EXC_TTL, 0);
++                      return NF_DROP;
+               }
++              ip_decrease_ttl(iph);
++      }
+-              /* Don't let conntrack code see this packet:
+-                   it will think we are starting a new
+-                   connection! --RR */
+-              ip_direct_send(*pskb);
++      if ((rt = route_mirror(*pskb, hooknum == NF_IP_LOCAL_IN)) == NULL)
++              return NF_DROP;
+-              return NF_STOLEN;
++      hh_len = (rt->u.dst.dev->hard_header_len + 15) & ~15;
++
++      /* Copy skb (even if skb is about to be dropped, we can't just
++       * clone it because there may be other things, such as tcpdump,
++       * interested in it). We also need to expand headroom in case
++       * hh_len of incoming interface < hh_len of outgoing interface */
++      nskb = skb_copy_expand(*pskb, hh_len, skb_tailroom(*pskb), GFP_ATOMIC);
++      if (nskb == NULL) {
++              dst_release(&rt->u.dst);
++              return NF_DROP;
+       }
++
++      dst_release(nskb->dst);
++      nskb->dst = &rt->u.dst;
++
++      ip_rewrite(nskb);
++      /* Don't let conntrack code see this packet:
++           it will think we are starting a new
++           connection! --RR */
++      ip_direct_send(nskb);
++
+       return NF_DROP;
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_NETLINK.c linux-2.4.20/net/ipv4/netfilter/ipt_NETLINK.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_NETLINK.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_NETLINK.c      Wed Sep 24 09:16:32 2003
+@@ -0,0 +1,119 @@
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/config.h>
++#include <linux/socket.h>
++#include <linux/skbuff.h>
++#include <linux/kernel.h>
++#include <linux/netlink.h>
++#include <linux/netdevice.h>
++#include <linux/mm.h>
++#include <linux/socket.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_NETLINK.h>
++#include <net/sock.h>
++
++MODULE_AUTHOR("Gianni Tedesco <gianni@ecsc.co.uk>");
++MODULE_DESCRIPTION("Provides iptables NETLINK target similar to ipchains -o");
++MODULE_LICENSE("GPL");
++
++#if 0
++#define DEBUGP        printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++static struct sock *ipfwsk;
++
++static unsigned int ipt_netlink_target(struct sk_buff **pskb,
++                                  unsigned int hooknum,
++                                  const struct net_device *in,
++                                  const struct net_device *out,
++                                  const void *targinfo, void *userinfo)
++{
++      struct ipt_nldata *nld = (struct ipt_nldata *)targinfo;
++      struct iphdr *ip = (*pskb)->nh.iph;
++      struct sk_buff *outskb;
++      struct netlink_t nlhdr;
++      size_t len=0;
++
++      /* Allocate a socket buffer */
++      if ( MASK(nld->flags, USE_SIZE) )
++              len = nld->size+sizeof(nlhdr);
++      else
++              len = ntohs(ip->tot_len)+sizeof(nlhdr); 
++
++      outskb=alloc_skb(len, GFP_ATOMIC);
++
++      if (outskb) {
++              nlhdr.len=len;
++              
++              if ( MASK(nld->flags, USE_MARK) )
++                      nlhdr.mark=(*pskb)->nfmark=nld->mark;
++              else
++                      nlhdr.mark=(*pskb)->nfmark;
++              
++              if ( in && in->name ) {
++                      strncpy((char *)&nlhdr.iface, in->name, IFNAMSIZ);
++              }else if ( out && out->name ){
++                      strncpy((char *)&nlhdr.iface, out->name, IFNAMSIZ);
++              }
++
++              skb_put(outskb, len);
++              memcpy(outskb->data, &nlhdr, sizeof(nlhdr));
++              memcpy((outskb->data)+sizeof(nlhdr), ip, len-sizeof(nlhdr));
++              netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_ATOMIC);
++      }else{
++              if (net_ratelimit())
++                      printk(KERN_WARNING "ipt_NETLINK: packet drop due to netlink failure\n");
++      }
++
++      if ( MASK(nld->flags, USE_DROP) )
++              return NF_DROP;
++
++      return IPT_CONTINUE;
++}
++
++static int ipt_netlink_checkentry(const char *tablename,
++                             const struct ipt_entry *e,
++                             void *targinfo,
++                             unsigned int targinfosize,
++                             unsigned int hookmask)
++{
++      //struct ipt_nldata *nld = (struct ipt_nldata *)targinfo;
++
++      return 1;
++}
++
++static struct ipt_target ipt_netlink_reg = { 
++      {NULL, NULL},
++      "NETLINK",
++      ipt_netlink_target,
++      ipt_netlink_checkentry,
++      NULL,
++      THIS_MODULE
++};
++
++static int __init init(void)
++{
++      DEBUGP("ipt_NETLINK: init module\n");   
++
++      if (ipt_register_target(&ipt_netlink_reg) != 0) {
++              return -EINVAL;
++      }
++
++      if ( !(ipfwsk=netlink_kernel_create(NETLINK_FIREWALL, NULL)) ){
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      DEBUGP("ipt_NETLINK: cleanup_module\n");
++      ipt_unregister_target(&ipt_netlink_reg);
++      if(ipfwsk->socket) sock_release(ipfwsk->socket);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_NETMAP.c linux-2.4.20/net/ipv4/netfilter/ipt_NETMAP.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_NETMAP.c   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_NETMAP.c       Wed Sep 24 09:16:34 2003
+@@ -0,0 +1,109 @@
++/* NETMAP - static NAT mapping of IP network addresses (1:1).
++   The mapping can be applied to source (POSTROUTING),
++   destination (PREROUTING), or both (with separate rules).
++
++   Author: Svenning Soerensen <svenning@post5.tele.dk>
++*/
++
++#include <linux/config.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++#include <linux/netdevice.h>
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++
++#define MODULENAME "NETMAP"
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Svenning Soerensen <svenning@post5.tele.dk>");
++MODULE_DESCRIPTION("iptables 1:1 NAT mapping of IP networks target");
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++static int
++check(const char *tablename,
++      const struct ipt_entry *e,
++      void *targinfo,
++      unsigned int targinfosize,
++      unsigned int hook_mask)
++{
++      const struct ip_nat_multi_range *mr = targinfo;
++
++      if (strcmp(tablename, "nat") != 0) {
++              DEBUGP(MODULENAME":check: bad table `%s'.\n", tablename);
++              return 0;
++      }
++      if (targinfosize != IPT_ALIGN(sizeof(*mr))) {
++              DEBUGP(MODULENAME":check: size %u.\n", targinfosize);
++              return 0;
++      }
++      if (hook_mask & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_POST_ROUTING))) {
++              DEBUGP(MODULENAME":check: bad hooks %x.\n", hook_mask);
++              return 0;
++      }
++      if (!(mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)) {
++              DEBUGP(MODULENAME":check: bad MAP_IPS.\n");
++              return 0;
++      }
++      if (mr->rangesize != 1) {
++              DEBUGP(MODULENAME":check: bad rangesize %u.\n", mr->rangesize);
++              return 0;
++      }
++      return 1;
++}
++
++static unsigned int
++target(struct sk_buff **pskb,
++       unsigned int hooknum,
++       const struct net_device *in,
++       const struct net_device *out,
++       const void *targinfo,
++       void *userinfo)
++{
++      struct ip_conntrack *ct;
++      enum ip_conntrack_info ctinfo;
++      u_int32_t new_ip, netmask;
++      const struct ip_nat_multi_range *mr = targinfo;
++      struct ip_nat_multi_range newrange;
++
++      IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
++                   || hooknum == NF_IP_POST_ROUTING);
++      ct = ip_conntrack_get(*pskb, &ctinfo);
++
++      netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip);
++
++      if (hooknum == NF_IP_PRE_ROUTING)
++              new_ip = (*pskb)->nh.iph->daddr & ~netmask;
++      else
++              new_ip = (*pskb)->nh.iph->saddr & ~netmask;
++      new_ip |= mr->range[0].min_ip & netmask;
++
++      newrange = ((struct ip_nat_multi_range)
++      { 1, { { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,
++               new_ip, new_ip,
++               mr->range[0].min, mr->range[0].max } } });
++
++      /* Hand modified range to generic setup. */
++      return ip_nat_setup_info(ct, &newrange, hooknum);
++}
++
++static struct ipt_target target_module
++= { { NULL, NULL }, MODULENAME, target, check, NULL,
++    THIS_MODULE };
++
++static int __init init(void)
++{
++      return ipt_register_target(&target_module);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&target_module);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_NOTRACK.c linux-2.4.20/net/ipv4/netfilter/ipt_NOTRACK.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_NOTRACK.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_NOTRACK.c      Wed Sep 24 09:18:12 2003
+@@ -0,0 +1,83 @@
++/* This is a module which is used for setting up fake conntracks
++ * on packets so that they are not seen by the conntrack/NAT code.
++ */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++#include <linux/netfilter_ipv4/ip_conntrack_core.h>
++#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
++
++static unsigned int
++target(struct sk_buff **pskb,
++       unsigned int hooknum,
++       const struct net_device *in,
++       const struct net_device *out,
++       const void *targinfo,
++       void *userinfo)
++{
++      struct ip_conntrack_protocol *proto;
++      struct ip_conntrack_tuple tuple;
++
++      if ((*pskb)->nfct != NULL)
++              return IPT_CONTINUE;
++
++      proto = ip_ct_find_proto((*pskb)->nh.iph->protocol);
++
++      if (ip_conntrack_get_tuple((*pskb)->nh.iph, (*pskb)->len, 
++                    &tuple, proto)
++          && !ip_conntrack_tuple_taken(&tuple, NULL)) {
++              (*pskb)->nfct = &ip_conntrack_untracked.infos[IP_CT_NEW];
++              nf_conntrack_get((*pskb)->nfct);
++      }
++
++      return IPT_CONTINUE;
++}
++
++static int
++checkentry(const char *tablename,
++         const struct ipt_entry *e,
++           void *targinfo,
++           unsigned int targinfosize,
++           unsigned int hook_mask)
++{
++      if (targinfosize != 0) {
++              printk(KERN_WARNING "NOTRACK: targinfosize %u != 0\n",
++                     targinfosize);
++              return 0;
++      }
++
++      if (strcmp(tablename, "raw") != 0) {
++              printk(KERN_WARNING "NOTRACK: can only be called from \"raw\" table, not \"%s\"\n", tablename);
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ipt_target ipt_notrack_reg = { 
++      .list = { NULL, NULL }, 
++      .name = "NOTRACK", 
++      .target = target, 
++      .checkentry = checkentry, 
++      .destroy = NULL, 
++      .me = THIS_MODULE 
++};
++
++static int __init init(void)
++{
++      if (ipt_register_target(&ipt_notrack_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&ipt_notrack_reg);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_POOL.c linux-2.4.20/net/ipv4/netfilter/ipt_POOL.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_POOL.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_POOL.c Wed Sep 24 09:16:59 2003
+@@ -0,0 +1,116 @@
++/* ipt_POOL.c - netfilter target to manipulate IP pools
++ *
++ * This target can be used almost everywhere. It acts on some specified
++ * IP pool, adding or deleting some IP address in the pool. The address
++ * can be either the source (--addsrc, --delsrc), or destination (--add/deldst)
++ * of the packet under inspection.
++ *
++ * The target normally returns IPT_CONTINUE.
++ */
++
++#include <linux/types.h>
++#include <linux/ip.h>
++#include <linux/timer.h>
++#include <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/netdevice.h>
++#include <linux/if.h>
++#include <linux/inetdevice.h>
++#include <net/protocol.h>
++#include <net/checksum.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++#include <linux/netfilter_ipv4/ipt_pool.h>
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++/*** NOTE NOTE NOTE NOTE ***
++**
++** By sheer luck, I get away with using the "struct ipt_pool_info", as defined
++** in <linux/netfilter_ipv4/ipt_pool.h>, both as the match and target info.
++** Here, in the target implementation, ipt_pool_info.src, if not IP_POOL_NONE,
++** is modified for the source IP address of the packet under inspection.
++** The same way, the ipt_pool_info.dst pool is modified for the destination.
++**
++** The address is added to the pool normally. However, if IPT_POOL_DEL_dir
++** flag is set in ipt_pool_info.flags, the address is deleted from the pool.
++**
++** If a modification was done to the pool, we possibly return ACCEPT or DROP,
++** if the right IPT_POOL_MOD_dir_ACCEPT or _MOD_dir_DROP flags are set.
++** The IPT_POOL_INV_MOD_dir flag inverts the sense of the check (i.e. the
++** ACCEPT and DROP flags are evaluated when the pool was not modified.)
++*/
++
++static int
++do_check(const char *tablename,
++             const struct ipt_entry *e,
++             void *targinfo,
++             unsigned int targinfosize,
++             unsigned int hook_mask)
++{
++      const struct ipt_pool_info *ipi = targinfo;
++
++      if (targinfosize != IPT_ALIGN(sizeof(*ipi))) {
++              DEBUGP("POOL_check: size %u.\n", targinfosize);
++              return 0;
++      }
++      DEBUGP("ipt_POOL:do_check(%d,%d,%d)\n",ipi->src,ipi->dst,ipi->flags);
++      return 1;
++}
++
++static unsigned int
++do_target(struct sk_buff **pskb,
++              unsigned int hooknum,
++              const struct net_device *in,
++              const struct net_device *out,
++              const void *targinfo,
++              void *userinfo)
++{
++      const struct ipt_pool_info *ipi = targinfo;
++      int modified;
++      unsigned int verdict = IPT_CONTINUE;
++
++      if (ipi->src != IP_POOL_NONE) {
++              modified = ip_pool_mod(ipi->src, ntohl((*pskb)->nh.iph->saddr),
++                                      ipi->flags & IPT_POOL_DEL_SRC);
++              if (!!modified ^ !!(ipi->flags & IPT_POOL_INV_MOD_SRC)) {
++                      if (ipi->flags & IPT_POOL_MOD_SRC_ACCEPT)
++                              verdict = NF_ACCEPT;
++                      else if (ipi->flags & IPT_POOL_MOD_SRC_DROP)
++                              verdict = NF_DROP;
++              }
++      }
++      if (verdict == IPT_CONTINUE && ipi->dst != IP_POOL_NONE) {
++              modified = ip_pool_mod(ipi->dst, ntohl((*pskb)->nh.iph->daddr),
++                                      ipi->flags & IPT_POOL_DEL_DST);
++              if (!!modified ^ !!(ipi->flags & IPT_POOL_INV_MOD_DST)) {
++                      if (ipi->flags & IPT_POOL_MOD_DST_ACCEPT)
++                              verdict = NF_ACCEPT;
++                      else if (ipi->flags & IPT_POOL_MOD_DST_DROP)
++                              verdict = NF_DROP;
++              }
++      }
++      return verdict;
++}
++
++static struct ipt_target pool_reg
++= { { NULL, NULL }, "POOL", do_target, do_check, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      DEBUGP("init ipt_POOL\n");
++      return ipt_register_target(&pool_reg);
++}
++
++static void __exit fini(void)
++{
++      DEBUGP("fini ipt_POOL\n");
++      ipt_unregister_target(&pool_reg);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_REJECT.c linux-2.4.20/net/ipv4/netfilter/ipt_REJECT.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_REJECT.c   Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ipt_REJECT.c       Wed Sep 24 09:18:09 2003
+@@ -1,15 +1,18 @@
+ /*
+  * This is a module which is used for rejecting packets.
+  * Added support for customized reject packets (Jozsef Kadlecsik).
++ * Added support for ICMP type-3-code-13 (Maciej Soltysiak). [RFC 1812]
++ * Added support for fake source IP in icmp-unreach (Fabrice MARIE & Guillaume MORIN).
+  */
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/skbuff.h>
+ #include <linux/ip.h>
++#include <linux/udp.h>
++#include <linux/icmp.h>
+ #include <net/icmp.h>
+ #include <net/ip.h>
+ #include <net/tcp.h>
+-struct in_device;
+ #include <net/route.h>
+ #include <linux/netfilter_ipv4/ip_tables.h>
+ #include <linux/netfilter_ipv4/ipt_REJECT.h>
+@@ -32,8 +35,49 @@
+               attach(new_skb, nfct);
+ }
++static inline struct rtable *route_reverse(struct sk_buff *skb, int hook)
++{
++      struct iphdr *iph = skb->nh.iph;
++      struct dst_entry *odst;
++      struct rt_key key = {};
++      struct rtable *rt;
++
++      if (hook != NF_IP_FORWARD) {
++              key.dst = iph->saddr;
++              if (hook == NF_IP_LOCAL_IN)
++                      key.src = iph->daddr;
++              key.tos = RT_TOS(iph->tos);
++
++              if (ip_route_output_key(&rt, &key) != 0)
++                      return NULL;
++      } else {
++              /* non-local src, find valid iif to satisfy
++               * rp-filter when calling ip_route_input. */
++              key.dst = iph->daddr;
++              if (ip_route_output_key(&rt, &key) != 0)
++                      return NULL;
++
++              odst = skb->dst;
++              if (ip_route_input(skb, iph->saddr, iph->daddr,
++                                 RT_TOS(iph->tos), rt->u.dst.dev) != 0) {
++                      dst_release(&rt->u.dst);
++                      return NULL;
++              }
++              dst_release(&rt->u.dst);
++              rt = (struct rtable *)skb->dst;
++              skb->dst = odst;
++      }
++
++      if (rt->u.dst.error) {
++              dst_release(&rt->u.dst);
++              rt = NULL;
++      }
++
++      return rt;
++}
++
+ /* Send RST reply */
+-static void send_reset(struct sk_buff *oldskb, int local)
++static void send_reset(struct sk_buff *oldskb, int hook)
+ {
+       struct sk_buff *nskb;
+       struct tcphdr *otcph, *tcph;
+@@ -42,6 +86,7 @@
+       u_int16_t tmp_port;
+       u_int32_t tmp_addr;
+       int needs_ack;
++      int hh_len;
+       /* IP header checks: fragment, too short. */
+       if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)
+@@ -61,12 +106,25 @@
+                        csum_partial((char *)otcph, otcplen, 0)) != 0)
+               return;
++      if ((rt = route_reverse(oldskb, hook)) == NULL)
++              return;
++
++      hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
++
++
+       /* Copy skb (even if skb is about to be dropped, we can't just
+            clone it because there may be other things, such as tcpdump,
+-           interested in it) */
+-      nskb = skb_copy(oldskb, GFP_ATOMIC);
+-      if (!nskb)
++           interested in it). We also need to expand headroom in case
++         hh_len of incoming interface < hh_len of outgoing interface */
++      nskb = skb_copy_expand(oldskb, hh_len, skb_tailroom(oldskb),
++                             GFP_ATOMIC);
++      if (!nskb) {
++              dst_release(&rt->u.dst);
+               return;
++      }
++
++      dst_release(nskb->dst);
++      nskb->dst = &rt->u.dst;
+       /* This packet will not be the same as the other: clear nf fields */
+       nf_conntrack_put(nskb->nfct);
+@@ -130,16 +188,6 @@
+       nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, 
+                                          nskb->nh.iph->ihl);
+-      /* Routing: if not headed for us, route won't like source */
+-      if (ip_route_output(&rt, nskb->nh.iph->daddr,
+-                          local ? nskb->nh.iph->saddr : 0,
+-                          RT_TOS(nskb->nh.iph->tos) | RTO_CONN,
+-                          0) != 0)
+-              goto free_nskb;
+-
+-      dst_release(nskb->dst);
+-      nskb->dst = &rt->u.dst;
+-
+       /* "Never happens" */
+       if (nskb->len > nskb->dst->pmtu)
+               goto free_nskb;
+@@ -154,12 +202,13 @@
+       kfree_skb(nskb);
+ }
+-static void send_unreach(struct sk_buff *skb_in, int code)
++static void send_unreach(struct sk_buff *skb_in, int code, u_int8_t fake_source_address)
+ {
+       struct iphdr *iph;
++      struct udphdr *udph;
+       struct icmphdr *icmph;
+       struct sk_buff *nskb;
+-      u32 saddr;
++      u32 saddr,packet_daddr;
+       u8 tos;
+       int hh_len, length;
+       struct rtable *rt = (struct rtable*)skb_in->dst;
+@@ -186,6 +235,19 @@
+       if (iph->frag_off&htons(IP_OFFSET))
+               return;
++      /* if UDP checksum is set, verify it's correct */
++      if (iph->protocol == IPPROTO_UDP
++          && skb_in->tail-(u8*)iph >= sizeof(struct udphdr)) {
++              int datalen = skb_in->len - (iph->ihl<<2);
++              udph = (struct udphdr *)((char *)iph + (iph->ihl<<2));
++              if (udph->check
++                  && csum_tcpudp_magic(iph->saddr, iph->daddr,
++                                       datalen, IPPROTO_UDP,
++                                       csum_partial((char *)udph, datalen,
++                                                    0)) != 0)
++                      return;
++      }
++                  
+       /* If we send an ICMP error to an ICMP error a mess would result.. */
+       if (iph->protocol == IPPROTO_ICMP
+           && skb_in->tail-(u8*)iph >= sizeof(struct icmphdr)) {
+@@ -201,7 +263,7 @@
+                       return;
+       }
+-      saddr = iph->daddr;
++      packet_daddr = saddr = iph->daddr;
+       if (!(rt->rt_flags & RTCF_LOCAL))
+               saddr = 0;
+@@ -244,7 +306,16 @@
+       iph->ttl = MAXTTL;
+       ip_select_ident(iph, &rt->u.dst, NULL);
+       iph->protocol=IPPROTO_ICMP;
+-      iph->saddr=rt->rt_src;
++
++        /* fake source IP if we have to
++           if fake_source_address == 1, we fake the source IP
++           from the packet destination address dynamically.
++        */
++        if (fake_source_address == 1)
++                iph->saddr = packet_daddr;
++        else
++              iph->saddr=rt->rt_src;
++
+       iph->daddr=rt->rt_dst;
+       iph->check=0;
+       iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+@@ -291,25 +362,28 @@
+          must return an absolute verdict. --RR */
+       switch (reject->with) {
+       case IPT_ICMP_NET_UNREACHABLE:
+-              send_unreach(*pskb, ICMP_NET_UNREACH);
++              send_unreach(*pskb, ICMP_NET_UNREACH, reject->fake_source_address);
+               break;
+       case IPT_ICMP_HOST_UNREACHABLE:
+-              send_unreach(*pskb, ICMP_HOST_UNREACH);
++              send_unreach(*pskb, ICMP_HOST_UNREACH, reject->fake_source_address);
+               break;
+       case IPT_ICMP_PROT_UNREACHABLE:
+-              send_unreach(*pskb, ICMP_PROT_UNREACH);
++              send_unreach(*pskb, ICMP_PROT_UNREACH, reject->fake_source_address);
+               break;
+       case IPT_ICMP_PORT_UNREACHABLE:
+-              send_unreach(*pskb, ICMP_PORT_UNREACH);
++              send_unreach(*pskb, ICMP_PORT_UNREACH, reject->fake_source_address);
+               break;
+       case IPT_ICMP_NET_PROHIBITED:
+-              send_unreach(*pskb, ICMP_NET_ANO);
++              send_unreach(*pskb, ICMP_NET_ANO, reject->fake_source_address);
+               break;
+       case IPT_ICMP_HOST_PROHIBITED:
+-              send_unreach(*pskb, ICMP_HOST_ANO);
++              send_unreach(*pskb, ICMP_HOST_ANO, reject->fake_source_address);
+               break;
++      case IPT_ICMP_ADMIN_PROHIBITED:
++              send_unreach(*pskb, ICMP_PKT_FILTERED, reject->fake_source_address);
++              break;
+       case IPT_TCP_RESET:
+-              send_reset(*pskb, hooknum == NF_IP_LOCAL_IN);
++              send_reset(*pskb, hooknum);
+       case IPT_ICMP_ECHOREPLY:
+               /* Doesn't happen. */
+               break;
+@@ -353,6 +427,11 @@
+                       DEBUGP("REJECT: TCP_RESET illegal for non-tcp\n");
+                       return 0;
+               }
++              /* cannot fake source address */
++              if (rejinfo->fake_source_address != 0) {
++                      DEBUGP("REJECT: fake-source-address illegal for TCP-RESET\n");
++                      return 0;
++              }
+       }
+       return 1;
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_ROUTE.c linux-2.4.20/net/ipv4/netfilter/ipt_ROUTE.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_ROUTE.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_ROUTE.c        Wed Sep 24 09:17:25 2003
+@@ -0,0 +1,369 @@
++/*
++ * This implements the ROUTE target, which enables you to setup unusual
++ * routes not supported by the standard kernel routing table.
++ *
++ * Copyright (C) 2002 Cedric de Launois <delaunois@info.ucl.ac.be>
++ *
++ * v 1.8 2003/07/25
++ *
++ * This software is distributed under GNU GPL v2, 1991
++ */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_ROUTE.h>
++#include <linux/netdevice.h>
++#include <linux/route.h>
++#include <net/ip.h>
++#include <net/route.h>
++#include <net/icmp.h>
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++
++/* Try to route the packet according to the routing keys specified in
++ * route_info. Keys are :
++ *  - ifindex : 
++ *      0 if no oif preferred, 
++ *      otherwise set to the index of the desired oif
++ *  - route_info->gw :
++ *      0 if no gateway specified,
++ *      otherwise set to the next host to which the pkt must be routed
++ * If success, skb->dev is the output device to which the packet must 
++ * be sent and skb->dst is not NULL
++ *
++ * RETURN: -1 if an error occured
++ *          1 if the packet was succesfully routed to the 
++ *            destination desired
++ *          0 if the kernel routing table could not route the packet
++ *            according to the keys specified
++ */
++static int route(struct sk_buff *skb,
++               unsigned int ifindex,
++               const struct ipt_route_target_info *route_info)
++{
++      int err;
++      struct rtable *rt;
++      struct iphdr *iph = skb->nh.iph;
++      struct rt_key key = { 
++              dst:iph->daddr,
++              src:0,
++              oif:ifindex, 
++              tos:RT_TOS(iph->tos) 
++      };
++      
++      /* The destination address may be overloaded by the target */
++      if (route_info->gw)
++              key.dst = route_info->gw;
++      
++      /* Trying to route the packet using the standard routing table. */
++      if ((err = ip_route_output_key(&rt, &key))) {
++              if (net_ratelimit()) 
++                      DEBUGP("ipt_ROUTE: couldn't route pkt (err: %i)",err);
++              return -1;
++      }
++      
++      /* Drop old route. */
++      dst_release(skb->dst);
++      skb->dst = NULL;
++
++      /* Success if no oif specified or if the oif correspond to the 
++       * one desired */
++      if (!ifindex || rt->u.dst.dev->ifindex == ifindex) {
++              skb->dst = &rt->u.dst;
++              skb->dev = skb->dst->dev;
++              return 1;
++      }
++      
++      /* The interface selected by the routing table is not the one
++       * specified by the user. This may happen because the dst address
++       * is one of our own addresses.
++       */
++      if (net_ratelimit()) 
++              DEBUGP("ipt_ROUTE: failed to route as desired gw=%u.%u.%u.%u oif=%i (got oif=%i)\n", 
++                     NIPQUAD(route_info->gw), ifindex, rt->u.dst.dev->ifindex);
++      
++      return 0;
++}
++
++
++/* Stolen from ip_finish_output2
++ * PRE : skb->dev is set to the device we are leaving by
++ *       skb->dst is not NULL
++ * POST: the packet is sent with the link layer header pushed
++ *       the packet is destroyed
++ */
++static void ip_direct_send(struct sk_buff *skb)
++{
++      struct dst_entry *dst = skb->dst;
++      struct hh_cache *hh = dst->hh;
++
++      if (hh) {
++              read_lock_bh(&hh->hh_lock);
++              memcpy(skb->data - 16, hh->hh_data, 16);
++              read_unlock_bh(&hh->hh_lock);
++              skb_push(skb, hh->hh_len);
++              hh->hh_output(skb);
++      } else if (dst->neighbour)
++              dst->neighbour->output(skb);
++      else {
++              if (net_ratelimit())
++                      DEBUGP(KERN_DEBUG "ipt_ROUTE: no hdr & no neighbour cache!\n");
++              kfree_skb(skb);
++      }
++}
++
++
++/* PRE : skb->dev is set to the device we are leaving by
++ * POST: - the packet is directly sent to the skb->dev device, without 
++ *         pushing the link layer header.
++ *       - the packet is destroyed
++ */
++static inline int dev_direct_send(struct sk_buff *skb)
++{
++      return dev_queue_xmit(skb);
++}
++
++
++static unsigned int route_oif(const struct ipt_route_target_info *route_info,
++                            struct sk_buff *skb) 
++{
++      unsigned int ifindex = 0;
++      struct net_device *dev_out = NULL;
++
++      /* The user set the interface name to use.
++       * Getting the current interface index.
++       */
++      if ((dev_out = dev_get_by_name(route_info->oif))) {
++              ifindex = dev_out->ifindex;
++      } else {
++              /* Unknown interface name : packet dropped */
++              if (net_ratelimit()) 
++                      DEBUGP("ipt_ROUTE: oif interface %s not found\n", route_info->oif);
++              return NF_DROP;
++      }
++
++      /* Trying the standard way of routing packets */
++      switch (route(skb, ifindex, route_info)) {
++      case 1:
++              dev_put(dev_out);
++              if (route_info->flags & IPT_ROUTE_CONTINUE)
++                      return IPT_CONTINUE;
++
++              ip_direct_send(skb);
++              return NF_STOLEN;
++
++      case 0:
++              /* Failed to send to oif. Trying the hard way */
++              if (route_info->flags & IPT_ROUTE_CONTINUE)
++                      return NF_DROP;
++
++              if (net_ratelimit()) 
++                      DEBUGP("ipt_ROUTE: forcing the use of %i\n",
++                             ifindex);
++
++              /* We have to force the use of an interface.
++               * This interface must be a tunnel interface since
++               * otherwise we can't guess the hw address for
++               * the packet. For a tunnel interface, no hw address
++               * is needed.
++               */
++              if ((dev_out->type != ARPHRD_TUNNEL)
++                  && (dev_out->type != ARPHRD_IPGRE)) {
++                      if (net_ratelimit()) 
++                              DEBUGP("ipt_ROUTE: can't guess the hw addr !\n");
++                      dev_put(dev_out);
++                      return NF_DROP;
++              }
++      
++              /* Send the packet. This will also free skb
++               * Do not go through the POST_ROUTING hook because 
++               * skb->dst is not set and because it will probably
++               * get confused by the destination IP address.
++               */
++              skb->dev = dev_out;
++              dev_direct_send(skb);
++              dev_put(dev_out);
++              return NF_STOLEN;
++              
++      default:
++              /* Unexpected error */
++              dev_put(dev_out);
++              return NF_DROP;
++      }
++}
++
++
++static unsigned int route_iif(const struct ipt_route_target_info *route_info,
++                            struct sk_buff *skb) 
++{
++      struct net_device *dev_out = NULL;
++      unsigned int ifindex = 0;
++
++      /* Getting the current interface index. */
++      if ((dev_out = dev_get_by_name(route_info->iif)))
++              ifindex = dev_out->ifindex;
++      else {
++              /* Unknown interface name : packet dropped */
++              if (net_ratelimit()) 
++                      DEBUGP("ipt_ROUTE: iif interface %s not found\n", route_info->oif);
++              return NF_DROP;
++      }
++
++      skb->dev = dev_out;
++      dst_release(skb->dst);
++      skb->dst = NULL;
++              
++      netif_rx(skb);
++
++      return NF_STOLEN;
++}
++
++
++static unsigned int route_gw(const struct ipt_route_target_info *route_info,
++                           struct sk_buff *skb) 
++{
++      if (route(skb, 0, route_info)!=1)
++              return NF_DROP;
++
++      if (route_info->flags & IPT_ROUTE_CONTINUE)
++              return IPT_CONTINUE;
++
++      ip_direct_send(skb);
++      return NF_STOLEN;
++}
++
++
++static unsigned int ipt_route_target(struct sk_buff **pskb,
++                                   unsigned int hooknum,
++                                   const struct net_device *in,
++                                   const struct net_device *out,
++                                   const void *targinfo,
++                                   void *userinfo)
++{
++      const struct ipt_route_target_info *route_info = targinfo;
++      struct sk_buff *skb = *pskb;
++
++      /* If we are at PREROUTING or INPUT hook
++       * the TTL isn't decreased by the IP stack
++       */
++      if (hooknum == NF_IP_PRE_ROUTING ||
++          hooknum == NF_IP_LOCAL_IN) {
++
++              struct iphdr *iph = skb->nh.iph;
++
++              if (iph->ttl <= 1) {
++                      struct rtable *rt;
++
++                      if (ip_route_output(&rt, iph->saddr, iph->daddr,
++                                          RT_TOS(iph->tos) | RTO_CONN,
++                                          0)) {
++                              return NF_DROP;
++                      }
++
++                      if (skb->dev == rt->u.dst.dev) {
++                              /* Drop old route. */
++                              dst_release(skb->dst);
++                              skb->dst = &rt->u.dst;
++
++                              /* this will traverse normal stack, and 
++                               * thus call conntrack on the icmp packet */
++                              icmp_send(skb, ICMP_TIME_EXCEEDED, 
++                                        ICMP_EXC_TTL, 0);
++                      }
++
++                      return NF_DROP;
++              }
++
++              ip_decrease_ttl(iph);
++      }
++
++      /* Tell conntrack to forget this packet since it may get confused 
++       * when a packet is leaving with dst address == our address.
++       * Good idea ? Dunno. Need advice.
++       */
++      if (!(route_info->flags & IPT_ROUTE_CONTINUE)) {
++              nf_conntrack_put(skb->nfct);
++              skb->nfct = NULL;
++              skb->nfcache = 0;
++#ifdef CONFIG_NETFILTER_DEBUG
++              skb->nf_debug = 0;
++#endif
++      }
++
++      if (route_info->oif[0]) 
++              return route_oif(route_info, *pskb);
++      
++      if (route_info->iif[0]) 
++              return route_iif(route_info, *pskb);
++
++      if (route_info->gw) 
++              return route_gw(route_info, *pskb);
++
++      if (net_ratelimit()) 
++              DEBUGP(KERN_DEBUG "ipt_ROUTE: no parameter !\n");
++
++      return IPT_CONTINUE;
++}
++
++
++static int ipt_route_checkentry(const char *tablename,
++                              const struct ipt_entry *e,
++                              void *targinfo,
++                              unsigned int targinfosize,
++                              unsigned int hook_mask)
++{
++      if (strcmp(tablename, "mangle") != 0) {
++              printk("ipt_ROUTE: bad table `%s', use the `mangle' table.\n",
++                     tablename);
++              return 0;
++      }
++
++      if (hook_mask & ~(  (1 << NF_IP_PRE_ROUTING)
++                          | (1 << NF_IP_LOCAL_IN)
++                          | (1 << NF_IP_FORWARD)
++                          | (1 << NF_IP_LOCAL_OUT)
++                          | (1 << NF_IP_POST_ROUTING))) {
++              printk("ipt_ROUTE: bad hook\n");
++              return 0;
++      }
++
++      if (targinfosize != IPT_ALIGN(sizeof(struct ipt_route_target_info))) {
++              printk(KERN_WARNING "ipt_ROUTE: targinfosize %u != %Zu\n",
++                     targinfosize,
++                     IPT_ALIGN(sizeof(struct ipt_route_target_info)));
++              return 0;
++      }
++
++      return 1;
++}
++
++
++static struct ipt_target ipt_route_reg
++= { { NULL, NULL }, "ROUTE", ipt_route_target, ipt_route_checkentry, NULL,
++    THIS_MODULE };
++
++
++static int __init init(void)
++{
++      if (ipt_register_target(&ipt_route_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&ipt_route_reg);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_SAME.c linux-2.4.20/net/ipv4/netfilter/ipt_SAME.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_SAME.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_SAME.c Wed Sep 24 09:16:38 2003
+@@ -0,0 +1,202 @@
++/* Same.  Just like SNAT, only try to make the connections
++ *      between client A and server B always have the same source ip.
++ *
++ * (C) 2000 Rusty Russell.  GPL.
++ *
++ * 010320 Martin Josefsson <gandalf@wlug.westbo.se>
++ *    * copied ipt_BALANCE.c to ipt_SAME.c and changed a few things.
++ * 010728 Martin Josefsson <gandalf@wlug.westbo.se>
++ *    * added --nodst to not include destination-ip in new source
++ *      calculations.
++ *    * added some more sanity-checks.
++ * 010729 Martin Josefsson <gandalf@wlug.westbo.se>
++ *    * fixed a buggy if-statement in same_check(), should have
++ *      used ntohl() but didn't.
++ *    * added support for multiple ranges. IPT_SAME_MAX_RANGE is
++ *      defined in linux/include/linux/netfilter_ipv4/ipt_SAME.h
++ *      and is currently set to 10.
++ *    * added support for 1-address range, nice to have now that
++ *      we have multiple ranges.
++ */
++#include <linux/types.h>
++#include <linux/ip.h>
++#include <linux/timer.h>
++#include <linux/module.h>
++#include <linux/netfilter.h>
++#include <linux/netdevice.h>
++#include <linux/if.h>
++#include <linux/inetdevice.h>
++#include <net/protocol.h>
++#include <net/checksum.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++#include <linux/netfilter_ipv4/ipt_SAME.h>
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Martin Josefsson <gandalf@wlug.westbo.se>");
++MODULE_DESCRIPTION("iptables special SNAT module for consistent sourceip");
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++static int
++same_check(const char *tablename,
++            const struct ipt_entry *e,
++            void *targinfo,
++            unsigned int targinfosize,
++            unsigned int hook_mask)
++{
++      unsigned int count, countess, rangeip, index = 0;
++      struct ipt_same_info *mr = targinfo;
++
++      mr->ipnum = 0;
++
++      if (strcmp(tablename, "nat") != 0) {
++              DEBUGP("same_check: bad table `%s'.\n", tablename);
++              return 0;
++      }
++      if (targinfosize != IPT_ALIGN(sizeof(*mr))) {
++              DEBUGP("same_check: size %u.\n", targinfosize);
++              return 0;
++      }
++      if (hook_mask & ~(1 << NF_IP_PRE_ROUTING | 1 << NF_IP_POST_ROUTING)) {
++              DEBUGP("same_check: bad hooks %x.\n", hook_mask);
++              return 0;
++      }
++      if (mr->rangesize < 1) {
++              DEBUGP("same_check: need at least one dest range.\n");
++              return 0;
++      }
++      if (mr->rangesize > IPT_SAME_MAX_RANGE) {
++              DEBUGP("same_check: too many ranges specified, maximum "
++                              "is %u ranges\n",
++                              IPT_SAME_MAX_RANGE);
++              return 0;
++      }
++      for (count = 0; count < mr->rangesize; count++) {
++              if (ntohl(mr->range[count].min_ip) >
++                              ntohl(mr->range[count].max_ip)) {
++                      DEBUGP("same_check: min_ip is larger than max_ip in "
++                              "range `%u.%u.%u.%u-%u.%u.%u.%u'.\n",
++                              NIPQUAD(mr->range[count].min_ip),
++                              NIPQUAD(mr->range[count].max_ip));
++                      return 0;
++              }
++              if (!(mr->range[count].flags & IP_NAT_RANGE_MAP_IPS)) {
++                      DEBUGP("same_check: bad MAP_IPS.\n");
++                      return 0;
++              }
++              rangeip = (ntohl(mr->range[count].max_ip) - 
++                                      ntohl(mr->range[count].min_ip) + 1);
++              mr->ipnum += rangeip;
++              
++              DEBUGP("same_check: range %u, ipnum = %u\n", count, rangeip);
++      }
++      DEBUGP("same_check: total ipaddresses = %u\n", mr->ipnum);
++      
++      mr->iparray = kmalloc((sizeof(u_int32_t) * mr->ipnum), GFP_KERNEL);
++      if (!mr->iparray) {
++              DEBUGP("same_check: Couldn't allocate %u bytes "
++                      "for %u ipaddresses!\n", 
++                      (sizeof(u_int32_t) * mr->ipnum), mr->ipnum);
++              return 0;
++      }
++      DEBUGP("same_check: Allocated %u bytes for %u ipaddresses.\n",
++                      (sizeof(u_int32_t) * mr->ipnum), mr->ipnum);
++      
++      for (count = 0; count < mr->rangesize; count++) {
++              for (countess = ntohl(mr->range[count].min_ip);
++                              countess <= ntohl(mr->range[count].max_ip);
++                                      countess++) {
++                      mr->iparray[index] = countess;
++                      DEBUGP("same_check: Added ipaddress `%u.%u.%u.%u' "
++                              "in index %u.\n",
++                              HIPQUAD(countess), index);
++                      index++;
++              }
++      }
++      return 1;
++}
++
++static void 
++same_destroy(void *targinfo,
++              unsigned int targinfosize)
++{
++      struct ipt_same_info *mr = targinfo;
++
++      kfree(mr->iparray);
++      
++      DEBUGP("same_destroy: Deallocated %u bytes for %u ipaddresses.\n",
++                      (sizeof(u_int32_t) * mr->ipnum), mr->ipnum);
++}
++
++static unsigned int
++same_target(struct sk_buff **pskb,
++              unsigned int hooknum,
++              const struct net_device *in,
++              const struct net_device *out,
++              const void *targinfo,
++              void *userinfo)
++{
++      struct ip_conntrack *ct;
++      enum ip_conntrack_info ctinfo;
++      u_int32_t tmpip, aindex, new_ip;
++      const struct ipt_same_info *mr = targinfo;
++      struct ip_nat_multi_range newrange;
++      const struct ip_conntrack_tuple *t;
++
++      IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING ||
++                      hooknum == NF_IP_POST_ROUTING);
++      ct = ip_conntrack_get(*pskb, &ctinfo);
++
++      t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++
++      /* Base new source on real src ip and optionally dst ip,
++         giving some hope for consistency across reboots.
++         Here we calculate the index in mr->iparray which
++         holds the ipaddress we should use */
++      
++      tmpip = ntohl(t->src.ip);
++
++      if (!(mr->info & IPT_SAME_NODST))
++              tmpip += ntohl(t->dst.ip);
++      
++      aindex = tmpip % mr->ipnum;
++              
++      new_ip = htonl(mr->iparray[aindex]);
++
++      DEBUGP("ipt_SAME: src=%u.%u.%u.%u dst=%u.%u.%u.%u, "
++                      "new src=%u.%u.%u.%u\n",
++                      NIPQUAD(t->src.ip), NIPQUAD(t->dst.ip),
++                      NIPQUAD(new_ip));
++
++      /* Transfer from original range. */
++      newrange = ((struct ip_nat_multi_range)
++              { 1, { { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,
++                       new_ip, new_ip,
++                       mr->range[0].min, mr->range[0].max } } });
++
++      /* Hand modified range to generic setup. */
++      return ip_nat_setup_info(ct, &newrange, hooknum);
++}
++
++static struct ipt_target same_reg
++= { { NULL, NULL }, "SAME", same_target, same_check, same_destroy,
++    THIS_MODULE };
++
++static int __init init(void)
++{
++      return ipt_register_target(&same_reg);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&same_reg);
++}
++
++module_init(init);
++module_exit(fini);
++
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_TARPIT.c linux-2.4.20/net/ipv4/netfilter/ipt_TARPIT.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_TARPIT.c   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_TARPIT.c       Wed Sep 24 09:17:45 2003
+@@ -0,0 +1,284 @@
++/* 
++ * Kernel module to capture and hold incoming TCP connections using 
++ * no local per-connection resources.
++ * 
++ * Based on ipt_REJECT.c and offering functionality similar to 
++ * LaBrea <http://www.hackbusters.net/LaBrea/>.
++ * 
++ * Copyright (c) 2002 Aaron Hopkins <tools@die.net>
++ * 
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ * 
++ * Goal:
++ * - Allow incoming TCP connections to be established.
++ * - Passing data should result in the connection being switched to the 
++ *   persist state (0 byte window), in which the remote side stops sending 
++ *   data and asks to continue every 60 seconds.
++ * - Attempts to shut down the connection should be ignored completely, so 
++ *   the remote side ends up having to time it out.
++ *
++ * This means:
++ * - Reply to TCP SYN,!ACK,!RST,!FIN with SYN-ACK, window 5 bytes
++ * - Reply to TCP SYN,ACK,!RST,!FIN with RST to prevent spoofing
++ * - Reply to TCP !SYN,!RST,!FIN with ACK, window 0 bytes, rate-limited
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <net/ip.h>
++#include <net/tcp.h>
++#include <net/icmp.h>
++struct in_device;
++#include <net/route.h>
++#include <linux/random.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++
++/* Stolen from ip_finish_output2 */
++static int ip_direct_send(struct sk_buff *skb)
++{
++      struct dst_entry *dst = skb->dst;
++      struct hh_cache *hh = dst->hh;
++
++      if (hh) {    
++              read_lock_bh(&hh->hh_lock);
++              memcpy(skb->data - 16, hh->hh_data, 16);
++                read_unlock_bh(&hh->hh_lock);
++                skb_push(skb, hh->hh_len);
++                return hh->hh_output(skb);
++        } else if (dst->neighbour)
++                return dst->neighbour->output(skb);
++
++      if (net_ratelimit())
++              printk(KERN_DEBUG "TARPIT ip_direct_send: no header cache and no neighbor!\n");
++        kfree_skb(skb);
++        return -EINVAL;
++}
++
++
++/* Send reply */
++static void tarpit_tcp(struct sk_buff *oskb,struct rtable *ort,int local)
++{
++      struct sk_buff *nskb;
++      struct rtable *nrt;
++      struct tcphdr *otcph, *ntcph;
++      unsigned int otcplen;
++      u_int16_t tmp;
++
++      /* A truncated TCP header isn't going to be useful */
++      if (oskb->len < (oskb->nh.iph->ihl*4) + sizeof(struct tcphdr))
++              return;
++
++      otcph = (struct tcphdr *)((u_int32_t*)oskb->nh.iph 
++                                  + oskb->nh.iph->ihl);
++      otcplen = oskb->len - oskb->nh.iph->ihl*4;
++
++      /* No replies for RST or FIN */
++      if (otcph->rst || otcph->fin)
++              return;
++
++      /* No reply to !SYN,!ACK.  Rate-limit replies to !SYN,ACKs */
++      if (!otcph->syn && (!otcph->ack || !xrlim_allow(&ort->u.dst, 1*HZ)))
++              return;
++
++      /* Check checksum. */
++      if (tcp_v4_check(otcph, otcplen, oskb->nh.iph->saddr,
++                       oskb->nh.iph->daddr,
++                       csum_partial((char *)otcph, otcplen, 0)) != 0)
++              return;
++
++      /* Copy skb (even if skb is about to be dropped, we can't just
++           clone it because there may be other things, such as tcpdump,
++           interested in it) */
++      nskb = skb_copy(oskb, GFP_ATOMIC);
++      if (!nskb)
++              return;
++
++      /* This packet will not be the same as the other: clear nf fields */
++      nf_conntrack_put(nskb->nfct);
++      nskb->nfct = NULL;
++      nskb->nfcache = 0;
++#ifdef CONFIG_NETFILTER_DEBUG
++      nskb->nf_debug = 0;
++#endif
++
++      ntcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
++
++      /* Truncate to length (no data) */
++      ntcph->doff = sizeof(struct tcphdr)/4;
++      skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));
++      nskb->nh.iph->tot_len = htons(nskb->len);
++
++      /* Swap source and dest */
++      nskb->nh.iph->daddr = xchg(&nskb->nh.iph->saddr, nskb->nh.iph->daddr);
++      tmp = ntcph->source;
++      ntcph->source = ntcph->dest;
++      ntcph->dest = tmp;
++
++      /* Use supplied sequence number or make a new one */
++      ntcph->seq = otcph->ack ? otcph->ack_seq 
++                     : htonl(secure_tcp_sequence_number(nskb->nh.iph->saddr, 
++                                                      nskb->nh.iph->daddr, 
++                                                      ntcph->source, 
++                                                      ntcph->dest));
++
++      /* Our SYN-ACKs must have a >0 window */
++      ntcph->window = (otcph->syn && !otcph->ack) ? htons(5) : 0;
++
++      ntcph->urg_ptr = 0;
++
++      /* Reset flags */
++      ((u_int8_t *)ntcph)[13] = 0;
++
++      if (otcph->syn && otcph->ack) {
++              ntcph->rst = 1;
++              ntcph->ack_seq = 0;
++      } else {
++              ntcph->syn = otcph->syn;
++              ntcph->ack = 1;
++              ntcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn);
++      }
++
++      /* Adjust TCP checksum */
++      ntcph->check = 0;
++      ntcph->check = tcp_v4_check(ntcph, sizeof(struct tcphdr),
++                                 nskb->nh.iph->saddr,
++                                 nskb->nh.iph->daddr,
++                                 csum_partial((char *)ntcph,
++                                              sizeof(struct tcphdr), 0));
++
++      /* Adjust IP TTL */
++      nskb->nh.iph->ttl = sysctl_ip_default_ttl;
++
++      /* Set DF, id = 0 */
++      nskb->nh.iph->frag_off = htons(IP_DF);
++      nskb->nh.iph->id = 0;
++
++      /* Adjust IP checksum */
++      nskb->nh.iph->check = 0;
++      nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, 
++                                         nskb->nh.iph->ihl);
++
++      if (ip_route_output(&nrt, nskb->nh.iph->daddr, 
++                          local ? nskb->nh.iph->saddr : 0,
++                          RT_TOS(nskb->nh.iph->tos) | RTO_CONN, 
++                          0) != 0)
++              goto free_nskb;
++
++      dst_release(nskb->dst);
++      nskb->dst = &nrt->u.dst;
++
++      /* "Never happens" */
++      if (nskb->len > nskb->dst->pmtu)
++              goto free_nskb;
++
++      ip_direct_send (nskb);
++
++      return;
++
++ free_nskb:
++      kfree_skb(nskb);
++}
++
++
++static unsigned int tarpit(struct sk_buff **pskb,
++                         unsigned int hooknum,
++                         const struct net_device *in,
++                         const struct net_device *out,
++                         const void *targinfo,
++                         void *userinfo)
++{
++      struct sk_buff *skb = *pskb;
++      struct rtable *rt = (struct rtable*)skb->dst;
++
++      /* Do we have an input route cache entry? */
++      if (!rt)
++              return NF_DROP;
++
++        /* No replies to physical multicast/broadcast */
++        if (skb->pkt_type != PACKET_HOST && skb->pkt_type != PACKET_OTHERHOST)
++              return NF_DROP;
++
++        /* Now check at the protocol level */
++      if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST))
++                return NF_DROP;
++
++      /* Our naive response construction doesn't deal with IP
++           options, and probably shouldn't try. */
++      if (skb->nh.iph->ihl*4 != sizeof(struct iphdr))
++              return NF_DROP;
++
++        /* We aren't interested in fragments */
++      if (skb->nh.iph->frag_off & htons(IP_OFFSET))
++                return NF_DROP;
++
++      tarpit_tcp(skb,rt,hooknum == NF_IP_LOCAL_IN);
++
++      return NF_DROP;
++}
++
++
++static int check(const char *tablename,
++               const struct ipt_entry *e,
++               void *targinfo,
++               unsigned int targinfosize,
++               unsigned int hook_mask)
++{
++      /* Only allow these for input/forward packet filtering. */
++      if (strcmp(tablename, "filter") != 0) {
++              DEBUGP("TARPIT: bad table %s'.\n", tablename);
++              return 0;
++      }
++      if ((hook_mask & ~((1 << NF_IP_LOCAL_IN) 
++                           | (1 << NF_IP_FORWARD))) != 0) {
++              DEBUGP("TARPIT: bad hook mask %X\n", hook_mask);
++              return 0;
++      }
++
++      /* Must specify that it's a TCP packet */
++      if (e->ip.proto != IPPROTO_TCP || (e->ip.invflags & IPT_INV_PROTO)) {
++              DEBUGP("TARPIT: not valid for non-tcp\n");
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ipt_target ipt_tarpit_reg
++= { { NULL, NULL }, "TARPIT", tarpit, check, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ipt_register_target(&ipt_tarpit_reg))
++              return -EINVAL;
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&ipt_tarpit_reg);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_TCPLAG.c linux-2.4.20/net/ipv4/netfilter/ipt_TCPLAG.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_TCPLAG.c   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_TCPLAG.c       Wed Sep 24 09:17:29 2003
+@@ -0,0 +1,697 @@
++/* ipt_TCPLAG.c -- kernel module to implement TCPLAG target into netfilter
++ * Copyright (C) 2002 Telford Tendys <telford@triode.net.au>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++/*
++ * This collects packets and attempts to make them into pairs
++ * based on its own knowledge of how typical network conversations
++ * operate. Once it has a pair, it logs the time between them.
++ */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <linux/spinlock.h>
++#include <net/icmp.h>
++#include <net/udp.h>
++#include <net/tcp.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++#include <net/route.h>
++#include <linux/netfilter_ipv4/ipt_TCPLAG.h>
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++/*
++ * We need one spinlock for the hash table.
++ */
++static spinlock_t hash_lock = SPIN_LOCK_UNLOCKED;
++
++typedef struct timeval timeval_T;
++
++/*
++ * Linked lists of events in the connection,
++ * these store the SEQ numbers and the newest is always
++ * at the start of the linked list, then they get older
++ * down to the end of the linked list (this is not perfect
++ * if packets get out of order but we don't worry about fine
++ * details like that).
++ *
++ * Matching any event wipes out that event and also all other
++ * events down the chain (i.e. all older events).
++ * This keeps the linked list as short as possible.
++ */
++typedef struct tcplag_event_S
++{
++      struct tcplag_event_S *next;
++      u16 source_port;
++      u16 dest_port;
++      u32 expected_ACK;
++      struct timeval stamp;
++} tcplag_event_T;
++
++/*
++ * This stores the connection statistics
++ * We define connections more loosely than TCP/IP does,
++ * because we only consider the two hosts, not the ports
++ * Also, we list the host-pairs in low,high order which
++ * means that we don't care who originated the connection.
++ */
++typedef struct tcplag_hash_S
++{
++      u32 low_ip;
++      u32 high_ip;
++      struct timeval lag_l_SEQ_h_ACK; /* l sends some data and h acknowledges that (sum of lag times) */
++      struct timeval lag_h_SEQ_l_ACK; /* h sends some data and l acknowledges that (sum of lag times) */
++      tcplag_event_T *h_ACK_list;     /* Try to match ACK packets coming from h in this list */
++      tcplag_event_T *l_ACK_list;     /* Try to match ACK packets coming from l in this list */
++      time_t stamp;                   /* When this bucket got added to the table */
++      u16 count_l_SEQ_h_ACK;          /* Increment for each event */
++      u16 count_h_SEQ_l_ACK;          /* Increment for each event */
++} tcplag_hash_T;
++
++static tcplag_hash_T **hashtab = 0;
++static u32 hashsize = 0;
++static u16 max_seconds = 30; /* Empty a hash bucket after this time */
++static u32 reaper_ix = 0;
++
++static void divide_down( timeval_T *T, int c )
++{
++      int remainder;
++
++      T->tv_usec /= c;
++      remainder = T->tv_sec % c; /* Only works properly with positive numbers */
++      remainder *= 1000000;
++      T->tv_usec == remainder;
++      T->tv_sec /= c;
++}
++
++int diff_timeval( timeval_T *tv1, timeval_T *tv2 )
++{
++      register long x;
++
++      x = tv1->tv_sec - tv2->tv_sec;
++      if( x ) return( x );
++      x = tv1->tv_usec - tv2->tv_usec;
++      return( x );
++}
++
++void sprint_timeval( char *buf, timeval_T *tv )
++{
++      if( tv->tv_sec )
++              sprintf( buf, "%lu%06lu", tv->tv_sec, tv->tv_usec );
++      else
++              sprintf( buf, "%lu", tv->tv_usec );
++}
++
++/*
++ * This generates the log messages through printk()
++ *
++ * There is really no particular interest in the port numbers at this stage,
++ * they are only useful for matching up the request with the reply.
++ * The IP numbers are useful because some sites may be slower than others
++ * or may travel different routes, etc (OK, in theory changing the port number
++ * could also change the route but I don't like that sort of theory).
++ *
++ * The tags are:
++ *
++ * LIP=          The IP number of the side with the lowest lag
++ * RIP=          The IP number of the side with the highest lag
++ * LLAG=         The average time (in us) between RIP->LIP SEQ and LIP->RIP ACK
++ * RLAG=         The average time (in us) between LIP->RIP SEQ and RIP->LIP ACK
++ */
++static void output( tcplag_hash_T *H, int level, const char *prefix )
++{
++      struct timeval ltm, rtm;
++      u32 local_ip, remote_ip;
++      char r_buf[ 20 ], l_buf[ 20 ];
++/*
++ * We can't make sense of a connection that only passes data one way,
++ * In principle, at least the SYN and FIN should go both ways so we
++ * should get a few hits for every connection.
++ */
++      if( 0 == H->count_l_SEQ_h_ACK || 0 == H->count_h_SEQ_l_ACK ) return;
++/*
++ * Calculate average times by dividing down
++ */
++      divide_down( &H->lag_l_SEQ_h_ACK, H->count_l_SEQ_h_ACK );
++      divide_down( &H->lag_h_SEQ_l_ACK, H->count_h_SEQ_l_ACK );
++/*
++ * Sort these two by the lag so the the local is always the short lag
++ */
++      if( diff_timeval( &H->lag_l_SEQ_h_ACK, &H->lag_h_SEQ_l_ACK ) > 0 )
++      {
++              local_ip    = H->low_ip;
++              remote_ip   = H->high_ip;
++              rtm.tv_sec  = H->lag_l_SEQ_h_ACK.tv_sec;
++              rtm.tv_usec = H->lag_l_SEQ_h_ACK.tv_usec;
++              ltm.tv_sec  = H->lag_h_SEQ_l_ACK.tv_sec;
++              ltm.tv_usec = H->lag_h_SEQ_l_ACK.tv_usec;
++      }
++      else
++      {
++              local_ip    = H->high_ip;
++              remote_ip   = H->low_ip;
++              ltm.tv_sec  = H->lag_l_SEQ_h_ACK.tv_sec;
++              ltm.tv_usec = H->lag_l_SEQ_h_ACK.tv_usec;
++              rtm.tv_sec  = H->lag_h_SEQ_l_ACK.tv_sec;
++              rtm.tv_usec = H->lag_h_SEQ_l_ACK.tv_usec;
++      }
++/*
++ * Don't use a spinlock on the output,
++ * it is not guaranteed safe because some OTHER printk could
++ * split our log message so we want only one single printk.
++ *
++ * We use sprintf() to partially pre-digest the output
++ *
++ * Actually, neither this not the main netfilter LOG target is
++ * really safe from printk() overlap, basically syslog cannot
++ * be regarded as a guaranteed data output channel. It is good
++ * enough for most purposes.
++ */
++      sprint_timeval( l_buf, &ltm );
++      sprint_timeval( r_buf, &rtm );
++      printk( "<%d>%sLIP=%u.%u.%u.%u RIP=%u.%u.%u.%u LLAG=%s RLAG=%s\n",
++                      level & 7, prefix,
++                      NIPQUAD( local_ip ), NIPQUAD( remote_ip ),
++                      l_buf, r_buf );
++}
++
++/*
++ * The reaper rolls through the hash table looking for old.
++ * Log entries are only generated at the reaping time
++ * (which means all log entries are out-of-date)
++ */
++static void reaper( time_t now, int level, const char *prefix )
++{
++      int i;
++
++      now -= max_seconds;
++      if( !hashsize ) return;
++      if( !hashtab ) return;
++      for( i = 0; i < 10; i++ )
++      {
++              if( ++reaper_ix >= hashsize ) reaper_ix = 0; 
++
++//            DEBUGP( KERN_WARNING "reaper checking %u\n", reaper_ix );
++
++              if( hashtab[ reaper_ix ])
++              {
++                      tcplag_hash_T *found = 0;
++
++                      spin_lock_bh( &hash_lock );
++                      if( hashtab[ reaper_ix ])
++                      {
++                              if( now > hashtab[ reaper_ix ]->stamp )
++                              {
++                                      DEBUGP( KERN_WARNING "reaper found expired entry\n" );
++                                      found = hashtab[ reaper_ix ];
++                                      hashtab[ reaper_ix ] = 0;
++                              }
++                      }
++                      spin_unlock_bh( &hash_lock );
++
++                      if( found )
++                      {
++                              output( found, level, prefix );
++                              kfree( found );
++                      }
++              }
++      }
++}
++
++/*
++ * Convert the connection characteristics into a number
++ * (not including the timestamp) FIXME: this is a sucky hash function
++ */
++static u32 make_hash( tcplag_hash_T *connection )
++{
++      register u32 r;
++
++      r = connection->low_ip;
++      r += connection->high_ip;
++      return( r );
++}
++
++static int compare_connections( tcplag_hash_T *con1, tcplag_hash_T *con2 )
++{
++      int x;
++
++      x = con1->low_ip - con2->low_ip; if( x ) return( x );
++      x = con1->high_ip - con2->high_ip;
++      return( x );
++}
++
++static int compare_events( tcplag_event_T *ev1, tcplag_event_T *ev2 )
++{
++      int x;
++
++      DEBUGP( "Comparing sequence %u to %u\n", ev1->expected_ACK, ev2->expected_ACK );
++      x = ev1->expected_ACK - ev2->expected_ACK;
++      if( x ) return( x );
++      DEBUGP( "Comparing source port %u to %u\n", ev1->source_port, ev2->source_port );
++      x = ev1->source_port - ev2->source_port;
++      if( x ) return( x );
++      DEBUGP( "Comparing destination port %u to %u\n", ev1->dest_port, ev2->dest_port );
++      x = ev1->dest_port - ev2->dest_port;
++      return( x );
++}
++
++/*
++ * Go to the hash table and either find an existing connection that
++ * matches correctly or inject a new connection into the table.
++ * Once the connection is OK, chain the event onto the linked list.
++ */
++static void hash_insert( tcplag_hash_T *connection, tcplag_event_T *event, int direction )
++{
++      u32 h, i;
++
++      if( !event ) return; /* Just to be safe */
++      if( !hashsize ) return;
++      if( !hashtab ) return;
++
++      h = make_hash( connection );
++      h %= hashsize;
++
++      DEBUGP( KERN_WARNING "hash_insert( %u )\n", h );
++
++      spin_lock_bh( &hash_lock );
++      for( i = 0; i < hashsize; i++, ({ if( ++h >= hashsize ) { h = 0; }}))
++      {
++              tcplag_hash_T *co_new = 0;
++/*
++ * Consider existing entry
++ */
++              if( hashtab[ h ])
++              {
++                      if( compare_connections( hashtab[ h ], connection )) continue;
++                      co_new = hashtab[ h ];
++                      DEBUGP( KERN_WARNING "Existing connection at %u\n", h );
++                      goto add_link;
++              }
++/*
++ * Use empty slot for new entry
++ */
++              if( !hashtab[ h ])
++              {
++                      co_new = kmalloc( sizeof( tcplag_hash_T ), GFP_ATOMIC );
++                      memset( co_new, 0, sizeof( tcplag_hash_T ));
++                      co_new->low_ip = connection->low_ip;
++                      co_new->high_ip = connection->high_ip;
++                      co_new->stamp = event->stamp.tv_sec;
++                      hashtab[ h ] = co_new;
++                      DEBUGP( KERN_WARNING "Added connection to table at %u\n", h );
++ add_link:
++                      {
++                              tcplag_event_T *ev_new;
++
++                              ev_new = kmalloc( sizeof( tcplag_event_T ), GFP_ATOMIC );
++                              memcpy( ev_new, event, sizeof( tcplag_event_T ));
++                              if( direction )
++                              {
++                                      ev_new->next = co_new->h_ACK_list;
++                                      co_new->h_ACK_list = ev_new;
++                                      DEBUGP( KERN_WARNING "Connection at %u, direction is h_ACK_list\n", h );
++                              }
++                              else
++                              {
++                                      ev_new->next = co_new->l_ACK_list;
++                                      co_new->l_ACK_list = ev_new;
++                                      DEBUGP( KERN_WARNING "Connection at %u, direction is l_ACK_list\n", h );
++                              }
++                      }
++                      goto done;
++              }
++      }
++ done:
++      spin_unlock_bh( &hash_lock );
++}
++
++/*
++ * Search the hash table for a matching connection,
++ * if we can't find one of those then we are stuffed.
++ *
++ * Once a connection has been found, scan along the list for
++ * a matching SEQ number and if that is found then calculate
++ * the lag, update the counters and cut the chain at the
++ * point where the matching SEQ is found.
++ */
++static int request_complete( tcplag_hash_T *connection, tcplag_event_T *event, int direction )
++{
++      u32 h, i;
++
++      if( !event ) return( 0 );
++      if( !hashsize ) return( 0 );
++      if( !hashtab ) return( 0 );
++      h = make_hash( connection );
++      h %= hashsize;
++
++      DEBUGP( KERN_WARNING "request_complete( %u )\n", h );
++
++      for( i = 0; i < hashsize; i++ )
++      {
++              tcplag_hash_T *found = 0;
++
++              if( !hashtab[ h ]) return( 0 );
++
++              spin_lock_bh( &hash_lock );
++              if( hashtab[ h ])
++              {
++                      if( !compare_connections( hashtab[ h ], connection ))
++                      {
++                              tcplag_event_T *ev, **evroot;
++                              timeval_T *tv;
++                              u16 *cn;
++
++                              found = hashtab[ h ];
++                              if( direction )
++                              {
++                                      evroot = &found->h_ACK_list;
++                                      tv = &found->lag_l_SEQ_h_ACK;
++                                      cn = &found->count_l_SEQ_h_ACK;
++                                      DEBUGP( KERN_WARNING "Connection at %u, direction is h_ACK_list\n", h );
++                              }
++                              else
++                              {
++                                      evroot = &found->l_ACK_list;
++                                      tv = &found->lag_h_SEQ_l_ACK;
++                                      cn = &found->count_h_SEQ_l_ACK;
++                                      DEBUGP( KERN_WARNING "Connection at %u, direction is l_ACK_list\n", h );
++                              }
++                              for( ev = *evroot; ev; ev = ev->next )
++                              {
++                                      if( !compare_events( ev, event ))
++                                      {
++/*
++ * Calculate the lag (in two parts) and add that to the collection
++ */
++                                              event->stamp.tv_sec -= ev->stamp.tv_sec;
++                                              event->stamp.tv_usec -= ev->stamp.tv_usec;
++                                              if( event->stamp.tv_usec < 0 )
++                                              {
++                                                      event->stamp.tv_usec += 1000000;
++                                                      event->stamp.tv_sec++;
++                                              }
++                                              if( event->stamp.tv_sec < 0 )
++                                              {
++                                                      DEBUGP( KERN_WARNING "Negative lag detected\n" );
++                                              }
++                                              else
++                                              {
++                                                      tv->tv_sec += event->stamp.tv_sec;
++                                                      tv->tv_usec += event->stamp.tv_usec;
++                                                      ++*cn;
++                                                      DEBUGP( KERN_WARNING "Found a match, added %lu.%06lu"
++                                                                      " (accumulator is up to %lu.%06lu, %u events)\n",
++                                                                      event->stamp.tv_sec,
++                                                                      event->stamp.tv_usec,
++                                                                      tv->tv_sec, tv->tv_usec, *cn );
++                                              }
++/*
++ * Truncate the linked list.
++ *
++ * Visit each event in the list and return the memory to the pool.
++ *
++ * If a host is making multiple connections to the same remote host
++ * then this truncation will result in some requests not being
++ * monitored. Statistically we will still get some reasonable number
++ * of measurements and multiple simultaneous connections between host
++ * pairs don't happen all that often.
++ */
++                                              *evroot = 0;
++                                              while( ev )
++                                              {
++                                                      tcplag_event_T *ev_next = ev->next;
++                                                      DEBUGP( KERN_WARNING "Shitcan %u\n", ev->expected_ACK );
++                                                      kfree( ev );
++                                                      ev = ev_next;
++                                              }
++/*
++ * TODO: overflow limit for *cn, force premature output() if necessary
++ * (and drop this connection from the hash table)
++ */
++                                              break;
++                                      }
++                              }
++                              goto done;
++                      }
++              }
++ done:
++              spin_unlock_bh( &hash_lock );
++
++              if( found ) return( 1 );
++              if( ++h >= hashsize ) h = 0;
++      }       
++      return( 0 );
++}
++
++/*
++ * Here is our target data:
++ *
++ * pskb      --  The packet itself (see linux/skbuff.h for breakdown)
++ *
++ * hooknum   --
++ *
++ * in        --  The device that this packet came in on
++ *               (depending on the chain this may or may not exist)
++ *
++ * out       --  The device that this packet is just about to go
++ *               out onto (again existance depends on the chain)
++ *
++ * targinfo  --  Our private data (handed through from iptables command util)
++ *
++ * userinfo  --  Some more data
++ *
++ */
++
++static unsigned int target( struct sk_buff **pskb,
++                                                      unsigned int hooknum,
++                                                      const struct net_device *in,
++                                                      const struct net_device *out,
++                                                      const void *targinfo,
++                                                      void *userinfo )
++{
++      struct iphdr *iph = ( *pskb )->nh.iph;
++      const struct ipt_tcplag *el = targinfo;
++      tcplag_hash_T connection;
++      tcplag_event_T event;
++      int direction;
++/*
++ * We know we are dealing with IP here
++ * Fill in all the obvious fields
++ */
++      if( iph->saddr > iph->daddr )
++      {
++              direction = 0;
++              connection.high_ip = iph->saddr;
++              connection.low_ip = iph->daddr;
++      }
++      else
++      {
++              direction = 1;
++              connection.low_ip = iph->saddr;
++              connection.high_ip = iph->daddr;
++      }
++      do_gettimeofday( &event.stamp );
++/*
++ * Do a bit of cleaning
++ */
++      reaper( event.stamp.tv_sec, el->level, el->prefix );
++
++      DEBUGP( KERN_WARNING "got packet %lu %lu %s %s\n",
++                      event.stamp.tv_sec,
++                      event.stamp.tv_usec,
++                      in ? in->name : "none", out ? out->name : "none" );
++/*
++ * Now start looking at the details
++ *
++ * First step is to identify this packet to see if it is 
++ * the sort of packet that we are interested in.
++ * Don't hold any locks while we are doing this because often
++ * we will just let the packet go without any further consideration.
++ */
++      switch( iph->protocol )
++      {
++              case IPPROTO_TCP:
++              {
++                      struct tcphdr *tcp;
++
++                      if( ntohs( iph->frag_off ) & IP_OFFSET )
++                      {
++                              DEBUGP( KERN_WARNING "ignoring fragment\n" );
++                              break;
++                      }
++                      tcp = (struct tcphdr *)((u32 *)iph + iph->ihl );
++                      event.source_port = ntohs( tcp->source );
++                      event.dest_port = ntohs( tcp->dest );
++/*
++ * Every packet should have a valid SEQ number so use this to
++ * generate an ACK number. This works along the formula:
++ * -- Start with the SEQ number
++ * -- For SYN or FIN add 1 to that number
++ * -- For data packet, add the data length to that number
++ */
++
++/*
++ * Data length requires a bit of fiddling around
++ */
++                      {
++                              unsigned int data_len;
++                              if( tcp->syn || tcp->fin )
++                              {
++                                      data_len = 1; /* Not real data, the SEQ clicks forward by 1 */
++                              }
++                              else
++                              {
++                                      data_len = ntohs( iph->tot_len );
++                                      data_len -= 4 * iph->ihl;  /* Subtract away IP header & options */
++                                      data_len -= 4 * tcp->doff; /* Subtract away TCP header & options */
++                              }
++                              
++                              DEBUGP( KERN_WARNING "Data length calculated at %u\n", data_len );
++
++                              if( data_len ) /* Only track events that demand an ACK */
++                              {
++                                      event.expected_ACK = ntohl( tcp->seq ) + data_len;
++                                      hash_insert( &connection, &event, direction );
++                              }
++                              else
++                              {
++                                      DEBUGP( "Don't bother to insert this, ACK not required\n" );
++                              }
++                      }
++
++                      if( tcp->ack )
++                      {
++/*
++ * Now we consider the matching of an existing event.
++ * Reverse the port numbers and change the ACK number to the actual ACK number
++ * Note that the direction is reversed because the reply will be going
++ * the opposite way to the request.
++ */
++                              event.expected_ACK = ntohl( tcp->ack_seq );
++                              event.dest_port = ntohs( tcp->source );
++                              event.source_port = ntohs( tcp->dest );
++                              request_complete( &connection, &event, !direction );
++                      }
++                      else
++                      {
++                              DEBUGP( "Don't bother to check this, ACK not valid\n" );
++                      }
++              }
++      }
++      return( IPT_CONTINUE );
++}
++
++/*
++ * return( 0 ) if there is a problem with this entry (i.e. kick it out of the kernel)
++ * return( 1 ) if the entry is suitable
++ *
++ * tablename     --  
++ *
++ * e             --  
++ *
++ * targinfo      --  Our private data block (handed to us from iptables plug-in)
++ *
++ * targinfosize  --  The size of our private data block
++ *
++ * hook_mask     --  
++ *
++ *
++ * Not much can go wrong for us, any illegal flags are harmlessly ignored,
++ * all possible flag combos make sense. All we check for is correct data size.
++ */
++static int checkentry( const char *tablename,
++                                         const struct ipt_entry *e,
++                                         void *targinfo,
++                                         unsigned int targinfosize,
++                                         unsigned int hook_mask )
++{
++      const struct ipt_tcplag *el = targinfo;
++
++      if( targinfosize != IPT_ALIGN( sizeof( struct ipt_tcplag )))
++      {
++              DEBUGP( "TCPLAG: targinfosize %u != %u\n", targinfosize,
++                              IPT_ALIGN( sizeof( struct ipt_tcplag )));
++              return( 0 );
++      }
++      if( el->prefix[ 14 ]) return( 0 ); /* Be sure to have terminated string */
++      return( 1 );
++}
++
++static struct ipt_target reg =
++{
++      { 0, 0 },
++      "TCPLAG",
++      &target,
++      &checkentry,
++      0,
++    THIS_MODULE
++};
++
++static int __init init( void )
++{
++      if( ipt_register_target( &reg )) return( -EINVAL );
++      hashsize = 123; /* should be configurable */
++      hashtab = kmalloc( sizeof( void * ) * hashsize, GFP_ATOMIC );
++      memset( hashtab, 0, sizeof( void * ) * hashsize );
++      return( 0 );
++}
++
++/*
++ * This should not need locks (in theory)
++ * because it can only get punted after it is no longer
++ * chained into any of the netfilter lists.
++ */
++static void __exit fini( void )
++{
++      int i;
++
++      ipt_unregister_target( &reg );
++/*
++ * Put back kernel memory
++ */
++      for( i = 0; i < hashsize; i++ )
++      {
++              tcplag_hash_T *p;
++
++              if(( p = hashtab[ i ]))
++              {
++                      tcplag_event_T *ev, *evn;
++
++                      hashtab[ i ] = 0;
++                      for( ev = p->h_ACK_list; ev; ev = evn )
++                      {
++                              evn = ev->next;
++                              kfree( ev );
++                      }
++                      for( ev = p->l_ACK_list; ev; ev = evn )
++                      {
++                              evn = ev->next;
++                              kfree( ev );
++                      }
++                      kfree( p );
++              }
++      }
++      kfree( hashtab );
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_TRACE.c linux-2.4.20/net/ipv4/netfilter/ipt_TRACE.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_TRACE.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_TRACE.c        Wed Sep 24 09:18:12 2003
+@@ -0,0 +1,66 @@
++/* This is a module which is used for setting 
++ * the NFC_TRACE flag in the nfcache field of an skb. 
++ */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++static unsigned int
++target(struct sk_buff **pskb,
++       unsigned int hooknum,
++       const struct net_device *in,
++       const struct net_device *out,
++       const void *targinfo,
++       void *userinfo)
++{
++      (*pskb)->nfcache |= NFC_TRACE;
++      return IPT_CONTINUE;
++}
++
++static int
++checkentry(const char *tablename,
++         const struct ipt_entry *e,
++           void *targinfo,
++           unsigned int targinfosize,
++           unsigned int hook_mask)
++{
++      if (targinfosize != 0) {
++              printk(KERN_WARNING "TRACE: targinfosize %u != 0\n",
++                     targinfosize);
++              return 0;
++      }
++
++      if (strcmp(tablename, "raw") != 0) {
++              printk(KERN_WARNING "TRACE: can only be called from \"raw\" table, not \"%s\"\n", tablename);
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ipt_target ipt_trace_reg = { 
++      .list = { NULL, NULL }, 
++      .name = "TRACE", 
++      .target = target, 
++      .checkentry = checkentry, 
++      .destroy = NULL, 
++      .me THIS_MODULE
++};
++
++static int __init init(void)
++{
++      if (ipt_register_target(&ipt_trace_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&ipt_trace_reg);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_TTL.c linux-2.4.20/net/ipv4/netfilter/ipt_TTL.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_TTL.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_TTL.c  Wed Sep 24 09:16:40 2003
+@@ -0,0 +1,110 @@
++/* TTL modification target for IP tables
++ * (C) 2000 by Harald Welte <laforge@gnumonks.org>
++ *
++ * Version: 1.8
++ *
++ * This software is distributed under the terms of GNU GPL
++ */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <net/checksum.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_TTL.h>
++
++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
++MODULE_DESCRIPTION("IP tables TTL modification module");
++MODULE_LICENSE("GPL");
++
++static unsigned int ipt_ttl_target(struct sk_buff **pskb, unsigned int hooknum,
++              const struct net_device *in, const struct net_device *out,
++              const void *targinfo, void *userinfo)
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      const struct ipt_TTL_info *info = targinfo;
++      u_int16_t diffs[2];
++      int new_ttl;
++                       
++      switch (info->mode) {
++              case IPT_TTL_SET:
++                      new_ttl = info->ttl;
++                      break;
++              case IPT_TTL_INC:
++                      new_ttl = iph->ttl + info->ttl;
++                      if (new_ttl > 255)
++                              new_ttl = 255;
++                      break;
++              case IPT_TTL_DEC:
++                      new_ttl = iph->ttl + info->ttl;
++                      if (new_ttl < 0)
++                              new_ttl = 0;
++                      break;
++              default:
++                      new_ttl = iph->ttl;
++                      break;
++      }
++
++      if (new_ttl != iph->ttl) {
++              diffs[0] = htons(((unsigned)iph->ttl) << 8) ^ 0xFFFF;
++              iph->ttl = new_ttl;
++              diffs[1] = htons(((unsigned)iph->ttl) << 8);
++              iph->check = csum_fold(csum_partial((char *)diffs,
++                                                  sizeof(diffs),
++                                                  iph->check^0xFFFF));
++                                                                                              (*pskb)->nfcache |= NFC_ALTERED;
++      }
++
++      return IPT_CONTINUE;
++}
++
++static int ipt_ttl_checkentry(const char *tablename,
++              const struct ipt_entry *e,
++              void *targinfo,
++              unsigned int targinfosize,
++              unsigned int hook_mask)
++{
++      struct ipt_TTL_info *info = targinfo;
++
++      if (targinfosize != IPT_ALIGN(sizeof(struct ipt_TTL_info))) {
++              printk(KERN_WARNING "TTL: targinfosize %u != %Zu\n",
++                              targinfosize,
++                              IPT_ALIGN(sizeof(struct ipt_TTL_info)));
++              return 0;       
++      }       
++
++      if (strcmp(tablename, "mangle")) {
++              printk(KERN_WARNING "TTL: can only be called from \"mangle\" table, not \"%s\"\n", tablename);
++              return 0;
++      }
++
++      if (info->mode > IPT_TTL_MAXMODE) {
++              printk(KERN_WARNING "TTL: invalid or unknown Mode %u\n", 
++                      info->mode);
++              return 0;
++      }
++
++      if ((info->mode != IPT_TTL_SET) && (info->ttl == 0)) {
++              printk(KERN_WARNING "TTL: increment/decrement doesn't make sense with value 0\n");
++              return 0;
++      }
++      
++      return 1;
++}
++
++static struct ipt_target ipt_TTL = { { NULL, NULL }, "TTL", 
++      ipt_ttl_target, ipt_ttl_checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      return ipt_register_target(&ipt_TTL);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&ipt_TTL);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_ULOG.c linux-2.4.20/net/ipv4/netfilter/ipt_ULOG.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_ULOG.c     Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ipt_ULOG.c Wed Sep 24 09:16:23 2003
+@@ -12,6 +12,7 @@
+  *          module loadtime -HW
+  * 2002/07/07 remove broken nflog_rcv() function -HW
+  * 2002/08/29 fix shifted/unshifted nlgroup bug -HW
++ * 2002/10/30 fix uninitialized mac_len field - <Anders K. Pedersen>
+  *
+  * Released under the terms of the GPL
+  *
+@@ -31,7 +32,7 @@
+  *   Specify, after how many clock ticks (intel: 100 per second) the queue
+  * should be flushed even if it is not full yet.
+  *
+- * ipt_ULOG.c,v 1.21 2002/08/29 10:54:34 laforge Exp
++ * ipt_ULOG.c,v 1.22 2002/10/30 09:07:31 laforge Exp
+  */
+ #include <linux/module.h>
+@@ -47,6 +48,7 @@
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_logging.h>
+ #include <linux/netfilter_ipv4/ipt_ULOG.h>
+ #include <linux/netfilter_ipv4/lockhelp.h>
+ #include <net/sock.h>
+@@ -76,6 +78,10 @@
+ MODULE_PARM(flushtimeout, "i");
+ MODULE_PARM_DESC(flushtimeout, "buffer flush timeout");
++static unsigned int nflog = 1;
++MODULE_PARM(nflog, "i");
++MODULE_PARM_DESC(nflog, "register as internal netfilter logging module");
++
+ /* global data structures */
+ typedef struct {
+@@ -153,17 +159,17 @@
+       return skb;
+ }
+-static unsigned int ipt_ulog_target(struct sk_buff **pskb,
+-                                  unsigned int hooknum,
+-                                  const struct net_device *in,
+-                                  const struct net_device *out,
+-                                  const void *targinfo, void *userinfo)
++static void ipt_ulog_packet(struct sk_buff **pskb,
++                          unsigned int hooknum,
++                          const struct net_device *in,
++                          const struct net_device *out,
++                          const struct ipt_ulog_info *loginfo,
++                          const char *prefix)
+ {
+       ulog_buff_t *ub;
+       ulog_packet_msg_t *pm;
+       size_t size, copy_len;
+       struct nlmsghdr *nlh;
+-      struct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) targinfo;
+       /* ffs == find first bit set, necessary because userspace
+        * is already shifting groupnumber, but we need unshifted.
+@@ -214,7 +220,9 @@
+       pm->timestamp_usec = (*pskb)->stamp.tv_usec;
+       pm->mark = (*pskb)->nfmark;
+       pm->hook = hooknum;
+-      if (loginfo->prefix[0] != '\0')
++      if (prefix != NULL)
++              strncpy(pm->prefix, prefix, sizeof(pm->prefix));
++      else if (loginfo->prefix[0] != '\0')
+               strncpy(pm->prefix, loginfo->prefix, sizeof(pm->prefix));
+       else
+               *(pm->prefix) = '\0';
+@@ -224,7 +232,8 @@
+           && in->hard_header_len <= ULOG_MAC_LEN) {
+               memcpy(pm->mac, (*pskb)->mac.raw, in->hard_header_len);
+               pm->mac_len = in->hard_header_len;
+-      }
++      } else
++              pm->mac_len = 0;
+       if (in)
+               strncpy(pm->indev_name, in->name, sizeof(pm->indev_name));
+@@ -260,8 +269,7 @@
+       UNLOCK_BH(&ulog_lock);
+-      return IPT_CONTINUE;
+-
++      return;
+ nlmsg_failure:
+       PRINTR("ipt_ULOG: error during NLMSG_PUT\n");
+@@ -270,8 +278,128 @@
+       PRINTR("ipt_ULOG: Error building netlink message\n");
+       UNLOCK_BH(&ulog_lock);
++}
+-      return IPT_CONTINUE;
++static unsigned int ipt_ulog_target(struct sk_buff **pskb,
++                                  unsigned int hooknum,
++                                  const struct net_device *in,
++                                  const struct net_device *out,
++                                  const void *targinfo, void *userinfo)
++{
++      struct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) targinfo;
++
++      ipt_ulog_packet(pskb, hooknum, in, out, loginfo, NULL);
++ 
++      return IPT_CONTINUE;
++}
++ 
++static void ip_ulog_packet_fn(struct sk_buff **pskb,
++                            unsigned int hooknum,
++                            const struct net_device *in,
++                            const struct net_device *out,
++                            const char *prefix)
++{
++      struct ipt_ulog_info loginfo = { 
++              .nl_group = ULOG_DEFAULT_NLGROUP,
++              .copy_range = 0,
++              .qthreshold = ULOG_DEFAULT_QTHRESHOLD,
++              .prefix = ""
++      };
++
++      ipt_ulog_packet(pskb, hooknum, in, out, &loginfo, prefix);
++}
++
++static void ip_ulog_fn(char *pfh, size_t len,
++                     const char *prefix)
++{
++      struct ipt_ulog_info loginfo = { 
++              .nl_group = ULOG_DEFAULT_NLGROUP,
++              .copy_range = 0,
++              .qthreshold = ULOG_DEFAULT_QTHRESHOLD,
++              .prefix = ""
++      };
++      ulog_buff_t *ub;
++      ulog_packet_msg_t *pm;
++      size_t size;
++      struct nlmsghdr *nlh;
++
++      /* ffs == find first bit set, necessary because userspace
++       * is already shifting groupnumber, but we need unshifted.
++       * ffs() returns [1..32], we need [0..31] */
++      unsigned int groupnum = ffs(loginfo.nl_group) - 1;
++
++      size = NLMSG_SPACE(sizeof(*pm) + len);
++
++      ub = &ulog_buffers[groupnum];
++      
++      LOCK_BH(&ulog_lock);
++
++      if (!ub->skb) {
++              if (!(ub->skb = ulog_alloc_skb(size)))
++                      goto alloc_failure;
++      } else if (ub->qlen >= loginfo.qthreshold ||
++                 size > skb_tailroom(ub->skb)) {
++              /* either the queue len is too high or we don't have 
++               * enough room in nlskb left. send it to userspace. */
++
++              ulog_send(groupnum);
++
++              if (!(ub->skb = ulog_alloc_skb(size)))
++                      goto alloc_failure;
++      }
++
++      DEBUGP("ipt_ULOG: qlen %d, qthreshold %d\n", ub->qlen, 
++              loginfo.qthreshold);
++
++      /* NLMSG_PUT contains a hidden goto nlmsg_failure !!! */
++      nlh = NLMSG_PUT(ub->skb, 0, ub->qlen, ULOG_NL_EVENT, 
++                      size - sizeof(*nlh));
++      ub->qlen++;
++
++      pm = NLMSG_DATA(nlh);
++
++      /* Set fake hook, prefix, timestamp etc. */
++      pm->data_len = len;
++      pm->timestamp_sec = 0;
++      pm->timestamp_usec = 0;
++      pm->mark = 0;
++      pm->hook = 0;
++      strncpy(pm->prefix, prefix, sizeof(pm->prefix));
++      pm->mac_len = 0;
++      pm->indev_name[0] = '\0';
++      pm->outdev_name[0] = '\0';
++      memcpy(pm->payload, pfh, len);
++      
++      /* check if we are building multi-part messages */
++      if (ub->qlen > 1) {
++              ub->lastnlh->nlmsg_flags |= NLM_F_MULTI;
++      }
++
++      /* if threshold is reached, send message to userspace */
++      if (qlen >= loginfo.qthreshold) {
++              if (loginfo.qthreshold > 1)
++                      nlh->nlmsg_type = NLMSG_DONE;
++      }
++
++      ub->lastnlh = nlh;
++
++      /* if timer isn't already running, start it */
++      if (!timer_pending(&ub->timer)) {
++              ub->timer.expires = jiffies + flushtimeout;
++              add_timer(&ub->timer);
++      }
++
++      UNLOCK_BH(&ulog_lock);
++
++      return;
++
++nlmsg_failure:
++      PRINTR("ipt_ULOG: error during NLMSG_PUT\n");
++
++alloc_failure:
++      PRINTR("ipt_ULOG: Error building netlink message\n");
++
++      UNLOCK_BH(&ulog_lock);
+ }
+ static int ipt_ulog_checkentry(const char *tablename,
+@@ -306,6 +434,8 @@
+     { {NULL, NULL}, "ULOG", ipt_ulog_target, ipt_ulog_checkentry, NULL,
+ THIS_MODULE
+ };
++static struct nf_logging_t ip_logging_fn
++= { ip_ulog_packet_fn, ip_ulog_fn };
+ static int __init init(void)
+ {
+@@ -320,7 +450,6 @@
+       /* initialize ulog_buffers */
+       for (i = 0; i < ULOG_MAXNLGROUPS; i++) {
+-              memset(&ulog_buffers[i], 0, sizeof(ulog_buff_t));
+               init_timer(&ulog_buffers[i].timer);
+               ulog_buffers[i].timer.function = ulog_timer;
+               ulog_buffers[i].timer.data = i;
+@@ -334,7 +463,9 @@
+               sock_release(nflognl->socket);
+               return -EINVAL;
+       }
+-
++      if (nflog)
++              nf_ip_log_register(&ip_logging_fn);
++      
+       return 0;
+ }
+@@ -345,6 +476,8 @@
+       DEBUGP("ipt_ULOG: cleanup_module\n");
++      if (nflog)
++              nf_ip_log_unregister(&ip_logging_fn);
+       ipt_unregister_target(&ipt_ulog_reg);
+       sock_release(nflognl->socket);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_XOR.c linux-2.4.20/net/ipv4/netfilter/ipt_XOR.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_XOR.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_XOR.c  Wed Sep 24 09:17:31 2003
+@@ -0,0 +1,107 @@
++/* XOR target for IP tables
++ * (C) 2000 by Tim Vandermeersch <Tim.Vandermeersch@pandora.be>
++ * Based on ipt_TTL.c
++ *
++ * Version 1.0
++ *
++ * This software is distributed under the terms of GNU GPL
++ */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <linux/tcp.h>
++#include <linux/udp.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_XOR.h>
++
++MODULE_AUTHOR("Tim Vandermeersch <Tim.Vandermeersch@pandora.be>");
++MODULE_DESCRIPTION("IP tables XOR module");
++MODULE_LICENSE("GPL");
++
++static unsigned int ipt_xor_target(struct sk_buff **pskb, unsigned int hooknum, 
++              const struct net_device *in, const struct net_device *out, 
++              const void *targinfo, void *userinfo)
++{
++      struct ipt_XOR_info *info = (void *) targinfo;
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct tcphdr *tcph;
++      struct udphdr *udph;
++      int i, j, k;
++  
++      if (iph->protocol == IPPROTO_TCP) {
++              tcph = (struct tcphdr *) ((*pskb)->data + iph->ihl*4);
++              for (i=0, j=0; i<(ntohs(iph->tot_len) - iph->ihl*4 - tcph->doff*4); ) {
++                      for (k=0; k<=info->block_size; k++) {
++                              (char) (*pskb)->data[ iph->ihl*4 + tcph->doff*4 + i ] ^= 
++                                              info->key[j];
++                              i++;
++                      }
++                      j++;
++                      if (info->key[j] == 0x00)
++                              j = 0;
++              }
++      } else if (iph->protocol == IPPROTO_UDP) {
++              udph = (struct udphdr *) ((*pskb)->data + iph->ihl*4);
++              for (i=0, j=0; i<(ntohs(udph->len)-8); ) {
++                      for (k=0; k<=info->block_size; k++) {
++                              (char) (*pskb)->data[ iph->ihl*4 + sizeof(struct udphdr) + i ] ^= 
++                                              info->key[j];
++                              i++;
++                      }
++                      j++;
++                      if (info->key[j] == 0x00)
++                              j = 0;
++              }
++      }
++  
++      return IPT_CONTINUE;
++}
++
++static int ipt_xor_checkentry(const char *tablename, const struct ipt_entry *e,
++              void *targinfo, unsigned int targinfosize, 
++              unsigned int hook_mask)
++{
++      struct ipt_XOR_info *info = targinfo;
++
++      if (targinfosize != IPT_ALIGN(sizeof(struct ipt_XOR_info))) {
++              printk(KERN_WARNING "XOR: targinfosize %u != %Zu\n", 
++                              targinfosize, IPT_ALIGN(sizeof(struct ipt_XOR_info)));
++      return 0;
++      }       
++
++      if (strcmp(tablename, "mangle")) {
++              printk(KERN_WARNING "XOR: can only be called from"
++                              "\"mangle\" table, not \"%s\"\n", tablename);
++              return 0; 
++      }
++
++      if (!strcmp(info->key, "")) {
++              printk(KERN_WARNING "XOR: You must specify a key");
++              return 0;
++      }
++
++      if (info->block_size == 0) {
++              printk(KERN_WARNING "XOR: You must specify a block-size");
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ipt_target ipt_XOR = { { NULL, NULL }, "XOR",
++      ipt_xor_target, ipt_xor_checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      return ipt_register_target(&ipt_XOR);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_target(&ipt_XOR);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_addrtype.c linux-2.4.20/net/ipv4/netfilter/ipt_addrtype.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_addrtype.c Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_addrtype.c     Wed Sep 24 09:17:32 2003
+@@ -0,0 +1,65 @@
++/*
++ *  iptables module to match inet_addr_type() of an ip.
++ */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netdevice.h>
++#include <net/route.h>
++
++#include <linux/netfilter_ipv4/ipt_addrtype.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++MODULE_LICENSE("GPL");
++
++static inline int match_type(u_int32_t addr, u_int16_t mask)
++{
++      return !!(mask & (1 << inet_addr_type(addr)));
++}
++
++static int match(const struct sk_buff *skb, const struct net_device *in,
++               const struct net_device *out, const void *matchinfo,
++               int offset, const void *hdr, u_int16_t datalen,
++               int *hotdrop)
++{
++      const struct ipt_addrtype_info *info = matchinfo;
++      const struct iphdr *iph = skb->nh.iph;
++      int ret = 1;
++
++      if (info->source)
++              ret &= match_type(iph->saddr, info->source)^info->invert_source;
++      if (info->dest)
++              ret &= match_type(iph->daddr, info->dest)^info->invert_dest;
++      
++      return ret;
++}
++
++static int checkentry(const char *tablename, const struct ipt_ip *ip,
++                    void *matchinfo, unsigned int matchsize,
++                    unsigned int hook_mask)
++{
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_addrtype_info))) {
++              printk(KERN_ERR "ipt_addrtype: invalid size (%u != %u)\n.",
++                     matchsize, IPT_ALIGN(sizeof(struct ipt_addrtype_info)));
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ipt_match addrtype_match = { { NULL, NULL }, "addrtype", &match,
++              &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      return ipt_register_match(&addrtype_match);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&addrtype_match);
++
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_ah.c linux-2.4.20/net/ipv4/netfilter/ipt_ah.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_ah.c       Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ipt_ah.c   Wed Sep 24 09:16:18 2003
+@@ -15,7 +15,11 @@
+ #endif
+ struct ahhdr {
++      __u8    nexthdr;
++      __u8    hdrlen;
++      __u16   reserved;
+       __u32   spi;
++      __u32   seq_no;
+ };
+ /* Returns 1 if the spi is matched by the range, 0 otherwise */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_condition.c linux-2.4.20/net/ipv4/netfilter/ipt_condition.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_condition.c        Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_condition.c    Wed Sep 24 09:17:34 2003
+@@ -0,0 +1,256 @@
++/*-------------------------------------------*\
++|          Netfilter Condition Module         |
++|                                             |
++|  Description: This module allows firewall   |
++|    rules to match using condition variables |
++|    stored in /proc files.                   |
++|                                             |
++|  Author: Stephane Ouellette     2002-10-22  |
++|          <ouellettes@videotron.ca>          |
++|                                             |
++|  History:                                   |
++|    2003-02-10  Second version with improved |
++|                locking and simplified code. |
++|                                             |
++|  This software is distributed under the     |
++|  terms of the GNU GPL.                      |
++\*-------------------------------------------*/
++
++#include<linux/module.h>
++#include<linux/proc_fs.h>
++#include<linux/spinlock.h>
++#include<linux/string.h>
++#include<asm/atomic.h>
++#include<linux/netfilter_ipv4/ip_tables.h>
++#include<linux/netfilter_ipv4/ipt_condition.h>
++
++
++#ifndef CONFIG_PROC_FS
++#error  "Proc file system support is required for this module"
++#endif
++
++
++MODULE_AUTHOR("Stephane Ouellette <ouellettes@videotron.ca>");
++MODULE_DESCRIPTION("Allows rules to match against condition variables");
++MODULE_LICENSE("GPL");
++
++
++struct condition_variable {
++      struct condition_variable *next;
++      struct proc_dir_entry *status_proc;
++      atomic_t refcount;
++        int enabled;   /* TRUE == 1, FALSE == 0 */
++};
++
++
++static rwlock_t list_lock;
++static struct condition_variable *head = NULL;
++static struct proc_dir_entry *proc_net_condition = NULL;
++
++
++static int
++ipt_condition_read_info(char *buffer, char **start, off_t offset,
++                      int length, int *eof, void *data)
++{
++      struct condition_variable *var =
++          (struct condition_variable *) data;
++
++      if (offset == 0) {
++              *start = buffer;
++              buffer[0] = (var->enabled) ? '1' : '0';
++              buffer[1] = '\n';
++              return 2;
++      }
++
++      *eof = 1;
++      return 0;
++}
++
++
++static int
++ipt_condition_write_info(struct file *file, const char *buffer,
++                       unsigned long length, void *data)
++{
++      struct condition_variable *var =
++          (struct condition_variable *) data;
++
++      if (length) {
++              /* Match only on the first character */
++              switch (buffer[0]) {
++              case '0':
++                      var->enabled = 0;
++                      break;
++              case '1':
++                      var->enabled = 1;
++              }
++      }
++
++      return (int) length;
++}
++
++
++static int
++match(const struct sk_buff *skb, const struct net_device *in,
++      const struct net_device *out, const void *matchinfo, int offset,
++      const void *hdr, u_int16_t datalen, int *hotdrop)
++{
++      const struct condition_info *info =
++          (const struct condition_info *) matchinfo;
++      struct condition_variable *var;
++      int condition_status = 0;
++
++      read_lock(&list_lock);
++
++      for (var = head; var; var = var->next) {
++              if (strcmp(info->name, var->status_proc->name) == 0) {
++                      condition_status = var->enabled;
++                      break;
++              }
++      }
++
++      read_unlock(&list_lock);
++
++      return condition_status ^ info->invert;
++}
++
++
++
++static int
++checkentry(const char *tablename, const struct ipt_ip *ip,
++         void *matchinfo, unsigned int matchsize, unsigned int hook_mask)
++{
++      struct condition_info *info = (struct condition_info *) matchinfo;
++      struct condition_variable *var, *newvar;
++
++      if (matchsize != IPT_ALIGN(sizeof(struct condition_info)))
++              return 0;
++
++      /* The first step is to check if the condition variable already exists. */
++      /* Here, a read lock is sufficient because we won't change the list */
++      read_lock(&list_lock);
++
++      for (var = head; var; var = var->next) {
++              if (strcmp(info->name, var->status_proc->name) == 0) {
++                      atomic_inc(&var->refcount);
++                      read_unlock(&list_lock);
++                      return 1;
++              }
++      }
++
++      read_unlock(&list_lock);
++
++      /* At this point, we need to allocate a new condition variable */
++      newvar = kmalloc(sizeof(struct condition_variable), GFP_KERNEL);
++
++      if (!newvar)
++              return -ENOMEM;
++
++      /* Create the condition variable's proc file entry */
++      newvar->status_proc = create_proc_entry(info->name, 0644, proc_net_condition);
++
++      if (!newvar->status_proc) {
++        /*
++         * There are two possibilities:
++         *  1- Another condition variable with the same name has been created, which is valid.
++         *  2- There was a memory allocation error.
++         */
++              kfree(newvar);
++              read_lock(&list_lock);
++
++              for (var = head; var; var = var->next) {
++                      if (strcmp(info->name, var->status_proc->name) == 0) {
++                              atomic_inc(&var->refcount);
++                              read_unlock(&list_lock);
++                              return 1;
++                      }
++              }
++
++              read_unlock(&list_lock);
++              return -ENOMEM;
++      }
++
++      atomic_set(&newvar->refcount, 1);
++      newvar->enabled = 0;
++      newvar->status_proc->owner = THIS_MODULE;
++      newvar->status_proc->data = newvar;
++      wmb();
++      newvar->status_proc->read_proc = ipt_condition_read_info;
++      newvar->status_proc->write_proc = ipt_condition_write_info;
++
++      write_lock(&list_lock);
++
++      newvar->next = head;
++      head = newvar;
++
++      write_unlock(&list_lock);
++
++      return 1;
++}
++
++
++static void
++destroy(void *matchinfo, unsigned int matchsize)
++{
++      struct condition_info *info = (struct condition_info *) matchinfo;
++      struct condition_variable *var, *prev = NULL;
++
++      if (matchsize != IPT_ALIGN(sizeof(struct condition_info)))
++              return;
++
++      write_lock(&list_lock);
++
++      for (var = head; var && strcmp(info->name, var->status_proc->name);
++           prev = var, var = var->next);
++
++      if (var && atomic_dec_and_test(&var->refcount)) {
++              if (prev)
++                      prev->next = var->next;
++              else
++                      head = var->next;
++
++              write_unlock(&list_lock);
++              remove_proc_entry(var->status_proc->name, proc_net_condition);
++              kfree(var);
++      } else
++              write_unlock(&list_lock);
++}
++
++
++static struct ipt_match condition_match = {
++      .name = "condition",
++      .match = &match,
++      .checkentry = &checkentry,
++      .destroy = &destroy,
++      .me = THIS_MODULE
++};
++
++
++static int __init
++init(void)
++{
++      int errorcode;
++
++      rwlock_init(&list_lock);
++      proc_net_condition = proc_mkdir("ipt_condition", proc_net);
++
++      if (proc_net_condition) {
++              errorcode = ipt_register_match(&condition_match);
++
++              if (errorcode)
++                      remove_proc_entry("ipt_condition", proc_net);
++      } else
++              errorcode = -EACCES;
++
++      return errorcode;
++}
++
++
++static void __exit
++fini(void)
++{
++      ipt_unregister_match(&condition_match);
++      remove_proc_entry("ipt_condition", proc_net);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_connlimit.c linux-2.4.20/net/ipv4/netfilter/ipt_connlimit.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_connlimit.c        Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_connlimit.c    Wed Sep 24 09:16:42 2003
+@@ -0,0 +1,232 @@
++/*
++ * netfilter module to limit the number of parallel tcp
++ * connections per IP address.
++ *   (c) 2000 Gerd Knorr <kraxel@bytesex.org>
++ *   Nov 2002: Martin Bene <martin.bene@icomedias.com>:
++ *            only ignore TIME_WAIT or gone connections
++ *
++ * based on ...
++ *
++ * Kernel module to match connection tracking information.
++ * GPL (C) 1999  Rusty Russell (rusty@rustcorp.com.au).
++ */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/list.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++#include <linux/netfilter_ipv4/ip_conntrack_core.h>
++#include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_connlimit.h>
++
++#define DEBUG 0
++
++MODULE_LICENSE("GPL");
++
++/* we'll save the tuples of all connections we care about */
++struct ipt_connlimit_conn
++{
++        struct list_head list;
++      struct ip_conntrack_tuple tuple;
++};
++
++struct ipt_connlimit_data {
++      spinlock_t lock;
++      struct list_head iphash[256];
++};
++
++static int ipt_iphash(u_int32_t addr)
++{
++      int hash;
++
++      hash  =  addr        & 0xff;
++      hash ^= (addr >>  8) & 0xff;
++      hash ^= (addr >> 16) & 0xff;
++      hash ^= (addr >> 24) & 0xff;
++      return hash;
++}
++
++static int count_them(struct ipt_connlimit_data *data,
++                    u_int32_t addr, u_int32_t mask,
++                    struct ip_conntrack *ct)
++{
++#if DEBUG
++      const static char *tcp[] = { "none", "established", "syn_sent", "syn_recv",
++                                   "fin_wait", "time_wait", "close", "close_wait",
++                                   "last_ack", "listen" };
++#endif
++      int addit = 1, matches = 0;
++      struct ip_conntrack_tuple tuple;
++      struct ip_conntrack_tuple_hash *found;
++      struct ipt_connlimit_conn *conn;
++      struct list_head *hash,*lh;
++
++      spin_lock(&data->lock);
++      tuple = ct->tuplehash[0].tuple;
++      hash = &data->iphash[ipt_iphash(addr & mask)];
++
++      /* check the saved connections */
++      for (lh = hash->next; lh != hash; lh = lh->next) {
++              conn = list_entry(lh,struct ipt_connlimit_conn,list);
++              found = ip_conntrack_find_get(&conn->tuple,ct);
++              if (0 == memcmp(&conn->tuple,&tuple,sizeof(tuple)) &&
++                  found != NULL &&
++                  found->ctrack->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT) {
++                      /* Just to be sure we have it only once in the list.
++                         We should'nt see tuples twice unless someone hooks this
++                         into a table without "-p tcp --syn" */
++                      addit = 0;
++              }
++#if DEBUG
++              printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d %s\n",
++                     ipt_iphash(addr & mask),
++                     NIPQUAD(conn->tuple.src.ip), ntohs(conn->tuple.src.u.tcp.port),
++                     NIPQUAD(conn->tuple.dst.ip), ntohs(conn->tuple.dst.u.tcp.port),
++                     (NULL != found) ? tcp[found->ctrack->proto.tcp.state] : "gone");
++#endif
++              if (NULL == found) {
++                      /* this one is gone */
++                      lh = lh->prev;
++                      list_del(lh->next);
++                      kfree(conn);
++                      continue;
++              }
++              if (found->ctrack->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT) {
++                      /* we don't care about connections which are
++                         closed already -> ditch it */
++                      lh = lh->prev;
++                      list_del(lh->next);
++                      kfree(conn);
++                      nf_conntrack_put(&found->ctrack->infos[0]);
++                      continue;
++              }
++              if ((addr & mask) == (conn->tuple.src.ip & mask)) {
++                      /* same source IP address -> be counted! */
++                      matches++;
++              }
++              nf_conntrack_put(&found->ctrack->infos[0]);
++      }
++      if (addit) {
++              /* save the new connection in our list */
++#if DEBUG
++              printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d new\n",
++                     ipt_iphash(addr & mask),
++                     NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port),
++                     NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port));
++#endif
++              conn = kmalloc(sizeof(*conn),GFP_ATOMIC);
++              if (NULL == conn)
++                      return -1;
++              memset(conn,0,sizeof(*conn));
++              INIT_LIST_HEAD(&conn->list);
++              conn->tuple = tuple;
++              list_add(&conn->list,hash);
++              matches++;
++      }
++      spin_unlock(&data->lock);
++      return matches;
++}
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      const struct ipt_connlimit_info *info = matchinfo;
++      int connections, match;
++      struct ip_conntrack *ct;
++      enum ip_conntrack_info ctinfo;
++
++      ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
++      if (NULL == ct) {
++              printk("ipt_connlimit: Oops: invalid ct state ?\n");
++              *hotdrop = 1;
++              return 0;
++      }
++      connections = count_them(info->data,skb->nh.iph->saddr,info->mask,ct);
++      if (-1 == connections) {
++              printk("ipt_connlimit: Hmm, kmalloc failed :-(\n");
++              *hotdrop = 1; /* let's free some memory :-) */
++              return 0;
++      }
++        match = (info->inverse) ? (connections <= info->limit) : (connections > info->limit);
++#if DEBUG
++      printk("ipt_connlimit: src=%u.%u.%u.%u mask=%u.%u.%u.%u "
++             "connections=%d limit=%d match=%s\n",
++             NIPQUAD(skb->nh.iph->saddr), NIPQUAD(info->mask),
++             connections, info->limit, match ? "yes" : "no");
++#endif
++
++      return match;
++}
++
++static int check(const char *tablename,
++               const struct ipt_ip *ip,
++               void *matchinfo,
++               unsigned int matchsize,
++               unsigned int hook_mask)
++{
++      struct ipt_connlimit_info *info = matchinfo;
++      int i;
++
++      /* verify size */
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_connlimit_info)))
++              return 0;
++
++      /* refuse anything but tcp */
++      if (ip->proto != IPPROTO_TCP)
++              return 0;
++
++      /* init private data */
++      info->data = kmalloc(sizeof(struct ipt_connlimit_data),GFP_KERNEL);
++      spin_lock_init(&(info->data->lock));
++      for (i = 0; i < 256; i++)
++              INIT_LIST_HEAD(&(info->data->iphash[i]));
++      
++      return 1;
++}
++
++static void destroy(void *matchinfo, unsigned int matchinfosize)
++{
++      struct ipt_connlimit_info *info = matchinfo;
++      struct ipt_connlimit_conn *conn;
++      struct list_head *hash;
++      int i;
++
++      /* cleanup */
++      for (i = 0; i < 256; i++) {
++              hash = &(info->data->iphash[i]);
++              while (hash != hash->next) {
++                      conn = list_entry(hash->next,struct ipt_connlimit_conn,list);
++                      list_del(hash->next);
++                      kfree(conn);
++              }
++      }
++      kfree(info->data);
++}
++
++static struct ipt_match connlimit_match
++= { { NULL, NULL }, "connlimit", &match, &check, &destroy, THIS_MODULE };
++
++static int __init init(void)
++{
++      /* NULL if ip_conntrack not a module */
++      if (ip_conntrack_module)
++              __MOD_INC_USE_COUNT(ip_conntrack_module);
++      return ipt_register_match(&connlimit_match);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&connlimit_match);
++      if (ip_conntrack_module)
++              __MOD_DEC_USE_COUNT(ip_conntrack_module);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_connmark.c linux-2.4.20/net/ipv4/netfilter/ipt_connmark.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_connmark.c Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_connmark.c     Wed Sep 24 09:17:17 2003
+@@ -0,0 +1,55 @@
++/* Kernel module to match connection mark values. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_connmark.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      const struct ipt_connmark_info *info = matchinfo;
++      enum ip_conntrack_info ctinfo;
++      struct ip_conntrack *ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
++      if (!ct)
++          return 0;
++
++      return ((ct->mark & info->mask) == info->mark) ^ info->invert;
++}
++
++static int
++checkentry(const char *tablename,
++           const struct ipt_ip *ip,
++           void *matchinfo,
++           unsigned int matchsize,
++           unsigned int hook_mask)
++{
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_connmark_info)))
++              return 0;
++
++      return 1;
++}
++
++static struct ipt_match connmark_match
++= { { NULL, NULL }, "connmark", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      return ipt_register_match(&connmark_match);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&connmark_match);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_conntrack.c linux-2.4.20/net/ipv4/netfilter/ipt_conntrack.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_conntrack.c        Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ipt_conntrack.c    Wed Sep 24 09:18:12 2003
+@@ -27,7 +27,13 @@
+ #define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg))
+-      statebit = ct ? IPT_CONNTRACK_STATE_INVALID : IPT_CONNTRACK_STATE_BIT(ctinfo);
++      if (skb->nfct == &ip_conntrack_untracked.infos[IP_CT_NEW])
++              statebit = IPT_CONNTRACK_STATE_UNTRACKED;
++      else if (ct)
++              statebit = IPT_CONNTRACK_STATE_BIT(ctinfo);
++      else
++              statebit = IPT_CONNTRACK_STATE_INVALID;
++ 
+       if(sinfo->flags & IPT_CONNTRACK_STATE) {
+               if (ct) {
+                       if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip !=
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_esp.c linux-2.4.20/net/ipv4/netfilter/ipt_esp.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_esp.c      Mon Feb 25 19:38:14 2002
++++ linux-2.4.20/net/ipv4/netfilter/ipt_esp.c  Wed Sep 24 09:16:18 2003
+@@ -16,6 +16,7 @@
+ struct esphdr {
+       __u32   spi;
++      __u32   seq_no;
+ };
+ /* Returns 1 if the spi is matched by the range, 0 otherwise */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_fuzzy.c linux-2.4.20/net/ipv4/netfilter/ipt_fuzzy.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_fuzzy.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_fuzzy.c        Wed Sep 24 09:16:44 2003
+@@ -0,0 +1,190 @@
++/*
++ *  This module implements a simple TSK FLC 
++ * (Takagi-Sugeno-Kang Fuzzy Logic Controller) that aims
++ * to limit , in an adaptive and flexible way , the packet rate crossing 
++ * a given stream . It serves as an initial and very simple (but effective)
++ * example of how Fuzzy Logic techniques can be applied to defeat DoS attacks.
++ *  As a matter of fact , Fuzzy Logic can help us to insert any "behavior"  
++ * into our code in a precise , adaptive and efficient manner. 
++ *  The goal is very similar to that of "limit" match , but using techniques of
++ * Fuzzy Control , that allow us to shape the transfer functions precisely ,
++ * avoiding over and undershoots - and stuff like that .
++ *
++ *
++ * 2002-08-10  Hime Aguiar e Oliveira Jr. <hime@engineer.com> : Initial version.
++ * 2002-08-17  : Changed to eliminate floating point operations .
++ * 2002-08-23  : Coding style changes .
++*/
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <linux/random.h>
++#include <net/tcp.h>
++#include <linux/spinlock.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_fuzzy.h>
++
++/*
++ Packet Acceptance Rate - LOW and Packet Acceptance Rate - HIGH
++ Expressed in percentage
++*/
++
++#define PAR_LOW               1/100
++#define PAR_HIGH      1
++
++static spinlock_t fuzzy_lock = SPIN_LOCK_UNLOCKED ;
++
++MODULE_AUTHOR("Hime Aguiar e Oliveira Junior <hime@engineer.com>");
++MODULE_DESCRIPTION("IP tables Fuzzy Logic Controller match module");
++MODULE_LICENSE("GPL");
++
++static  u_int8_t mf_high(u_int32_t tx,u_int32_t mini,u_int32_t maxi)
++{
++      if (tx >= maxi) return 100;
++
++      if (tx <= mini) return 0;
++
++      return ( (100*(tx-mini)) / (maxi-mini) ) ;
++}
++
++static u_int8_t mf_low(u_int32_t tx,u_int32_t mini,u_int32_t maxi)
++{
++      if (tx <= mini) return 100;
++
++      if (tx >= maxi) return 0;
++
++      return ( (100*( maxi - tx ))  / ( maxi - mini ) ) ;
++
++}
++
++static int
++ipt_fuzzy_match(const struct sk_buff *pskb,
++             const struct net_device *in,
++             const struct net_device *out,
++             const void *matchinfo,
++             int offset,
++             const void *hdr,
++             u_int16_t datalen,
++             int *hotdrop)
++{
++      /* From userspace */
++      
++      struct ipt_fuzzy_info *info = (struct ipt_fuzzy_info *) matchinfo;
++
++      u_int8_t random_number;
++      unsigned long amount ;
++      u_int8_t howhigh , howlow ;
++      
++
++      spin_lock_bh(&fuzzy_lock) ; /* Rise the lock */
++
++      info->bytes_total += pskb->len ;
++      info->packets_total++ ;
++
++      info->present_time = jiffies ;
++      
++      if ( info->present_time >= info->previous_time )
++              amount = info->present_time - info->previous_time ;
++      else { 
++              /* There was a transition : I choose to re-sample 
++                 and keep the old acceptance rate...
++              */
++
++              amount = 0 ;
++              info->previous_time = info->present_time ;
++              info->bytes_total = info->packets_total = 0;
++           };
++      
++      if (  amount > HZ/10 ) /* More than 100 ms elapsed ... */
++              {
++
++      info->mean_rate = (u_int32_t) ( ( HZ * info->packets_total )  \
++                                      / amount ) ;
++
++              info->previous_time = info->present_time ;
++              info->bytes_total = info->packets_total = 0 ;
++
++       howhigh = mf_high(info->mean_rate,info->minimum_rate,info->maximum_rate);
++       howlow  = mf_low(info->mean_rate,info->minimum_rate,info->maximum_rate);
++
++    info->acceptance_rate = (u_int8_t) \
++                         ( howhigh*PAR_LOW + PAR_HIGH*howlow ) ;
++
++    /* In fact , the above defuzzification would require a denominator
++       proportional to (howhigh+howlow) but , in this particular case ,
++       that expression is constant .
++        An imediate consequence is that it isn't necessary to call 
++       both mf_high and mf_low - but to keep things understandable ,
++       I did so .
++     */ 
++
++              }
++      
++      spin_unlock_bh(&fuzzy_lock) ; /* Release the lock */
++
++
++      if ( info->acceptance_rate < 100 )
++      {                
++              get_random_bytes((void *)(&random_number), 1);
++
++              /*  If within the acceptance , it can pass => don't match */
++              if ( random_number <= (255 * info->acceptance_rate) / 100 )
++                      return 0 ;
++              else
++                      return 1; /* It can't pass ( It matches ) */
++      } ;
++
++      return 0; /* acceptance_rate == 100 % => Everything passes ... */
++      
++}
++
++static int
++ipt_fuzzy_checkentry(const char *tablename,
++                 const struct ipt_ip *e,
++                 void *matchinfo,
++                 unsigned int matchsize,
++                 unsigned int hook_mask)
++{
++      
++      const struct ipt_fuzzy_info *info = matchinfo;
++
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_fuzzy_info))) {
++              printk("ipt_fuzzy: matchsize %u != %u\n", matchsize,
++                     IPT_ALIGN(sizeof(struct ipt_fuzzy_info)));
++              return 0;
++      }
++
++if ((info->minimum_rate < MINFUZZYRATE ) || (info->maximum_rate > MAXFUZZYRATE)
++      || (info->minimum_rate >= info->maximum_rate ))
++              {
++              printk("ipt_fuzzy: BAD limits , please verify !!!\n");
++              return 0;
++              }
++
++      return 1;
++}
++
++static struct ipt_match ipt_fuzzy_reg = { 
++      {NULL, NULL},
++      "fuzzy",
++      ipt_fuzzy_match,
++      ipt_fuzzy_checkentry,
++      NULL,
++      THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ipt_register_match(&ipt_fuzzy_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&ipt_fuzzy_reg);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_helper.c linux-2.4.20/net/ipv4/netfilter/ipt_helper.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_helper.c   Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ipt_helper.c       Wed Sep 24 09:16:17 2003
+@@ -10,6 +10,7 @@
+ #include <linux/module.h>
+ #include <linux/skbuff.h>
+ #include <linux/netfilter_ipv4/ip_conntrack.h>
++#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+ #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+ #include <linux/netfilter_ipv4/ip_tables.h>
+ #include <linux/netfilter_ipv4/ipt_helper.h>
+@@ -36,6 +37,7 @@
+       struct ip_conntrack_expect *exp;
+       struct ip_conntrack *ct;
+       enum ip_conntrack_info ctinfo;
++      int ret = 0;
+       
+       ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
+       if (!ct) {
+@@ -49,23 +51,27 @@
+       }
+       exp = ct->master;
++      READ_LOCK(&ip_conntrack_lock);
+       if (!exp->expectant) {
+               DEBUGP("ipt_helper: expectation %p without expectant !?!\n", 
+                       exp);
+-              return 0;
++              goto out_unlock;
+       }
+       if (!exp->expectant->helper) {
+               DEBUGP("ipt_helper: master ct %p has no helper\n", 
+                       exp->expectant);
+-              return 0;
++              goto out_unlock;
+       }
+       DEBUGP("master's name = %s , info->name = %s\n", 
+               exp->expectant->helper->name, info->name);
+-      return !strncmp(exp->expectant->helper->name, info->name, 
+-                      strlen(exp->expectant->helper->name)) ^ info->invert;
++      ret = !strncmp(exp->expectant->helper->name, info->name, 
++                     strlen(exp->expectant->helper->name)) ^ info->invert;
++out_unlock:
++      READ_UNLOCK(&ip_conntrack_lock);
++      return ret;
+ }
+ static int check(const char *tablename,
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_iprange.c linux-2.4.20/net/ipv4/netfilter/ipt_iprange.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_iprange.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_iprange.c      Wed Sep 24 09:16:47 2003
+@@ -0,0 +1,101 @@
++/*
++ * iptables module to match IP address ranges
++ *   (c) 2003 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
++ *
++ * Released under the terms of GNU GPLv2.
++ *
++ */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_iprange.h>
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
++MODULE_DESCRIPTION("iptables arbitrary IP range match module");
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      const struct ipt_iprange_info *info = matchinfo;
++      const struct iphdr *iph = skb->nh.iph;
++      
++
++      if (info->flags & IPRANGE_SRC) {
++              if (((ntohl(iph->saddr) < ntohl(info->src.min_ip))
++                        || (ntohl(iph->saddr) > ntohl(info->src.max_ip)))
++                       ^ !!(info->flags & IPRANGE_SRC_INV)) {
++                      DEBUGP("src IP %u.%u.%u.%u NOT in range %s"
++                             "%u.%u.%u.%u-%u.%u.%u.%u\n",
++                              NIPQUAD(iph->saddr),
++                              info->flags & IPRANGE_SRC_INV ? "(INV) " : "",
++                              NIPQUAD(info->src.min_ip),
++                              NIPQUAD(info->src.max_ip));
++                      return 0;
++              }
++      }
++      if (info->flags & IPRANGE_DST) {
++              if (((ntohl(iph->daddr) < ntohl(info->dst.min_ip))
++                        || (ntohl(iph->daddr) > ntohl(info->dst.max_ip)))
++                       ^ !!(info->flags & IPRANGE_DST_INV)) {
++                      DEBUGP("dst IP %u.%u.%u.%u NOT in range %s"
++                             "%u.%u.%u.%u-%u.%u.%u.%u\n",
++                              NIPQUAD(iph->daddr),
++                              info->flags & IPRANGE_DST_INV ? "(INV) " : "",
++                              NIPQUAD(info->dst.min_ip),
++                              NIPQUAD(info->dst.max_ip));
++                      return 0;
++              }
++      }
++      return 1;
++}
++
++static int check(const char *tablename,
++               const struct ipt_ip *ip,
++               void *matchinfo,
++               unsigned int matchsize,
++               unsigned int hook_mask)
++{
++      /* verify size */
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_iprange_info)))
++              return 0;
++
++      return 1;
++}
++
++static struct ipt_match iprange_match = 
++{ 
++      .list = { NULL, NULL }, 
++      .name = "iprange", 
++      .match = &match, 
++      .checkentry = &check, 
++      .destroy = NULL, 
++      .me = THIS_MODULE
++};
++
++static int __init init(void)
++{
++      return ipt_register_match(&iprange_match);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&iprange_match);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_ipv4options.c linux-2.4.20/net/ipv4/netfilter/ipt_ipv4options.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_ipv4options.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_ipv4options.c  Wed Sep 24 09:16:49 2003
+@@ -0,0 +1,170 @@
++/*
++  This is a module which is used to match ipv4 options.
++  This file is distributed under the terms of the GNU General Public
++  License (GPL). Copies of the GPL can be obtained from:
++  ftp://prep.ai.mit.edu/pub/gnu/GPL
++
++  11-mars-2001 Fabrice MARIE <fabrice@netfilter.org> : initial development.
++  12-july-2001 Fabrice MARIE <fabrice@netfilter.org> : added router-alert otions matching. Fixed a bug with no-srr
++  12-august-2001 Imran Patel <ipatel@crosswinds.net> : optimization of the match.
++  18-november-2001 Fabrice MARIE <fabrice@netfilter.org> : added [!] 'any' option match.
++*/
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <net/ip.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_ipv4options.h>
++
++MODULE_LICENSE("GPL");
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      const struct ipt_ipv4options_info *info = matchinfo;   /* match info for rule */
++      const struct iphdr *iph = skb->nh.iph;
++      const struct ip_options *opt;
++
++      if (iph->ihl * 4 == sizeof(struct iphdr)) {
++              /* No options, so we match only the "DONTs" and the "IGNOREs" */
++
++              if (((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT) ||
++                  ((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) ||
++                  ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR) ||
++                  ((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) ||
++                  ((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) ||
++                    ((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT))
++                      return 0;
++              return 1;
++      }
++      else {
++              if ((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT)
++                      /* there are options, and we don't need to care which one */
++                      return 1;
++              else {
++                      if ((info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) == IPT_IPV4OPTION_DONT_MATCH_ANY_OPT)
++                              /* there are options but we don't want any ! */
++                              return 0;
++              }
++      }
++
++      opt = &(IPCB(skb)->opt);
++
++      /* source routing */
++      if ((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) {
++              if (!((opt->srr) & (opt->is_strictroute)))
++                      return 0;
++      }
++      else if ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR) {
++              if (!((opt->srr) & (!opt->is_strictroute)))
++                      return 0;
++      }
++      else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_SRR) == IPT_IPV4OPTION_DONT_MATCH_SRR) {
++              if (opt->srr)
++                      return 0;
++      }
++      /* record route */
++      if ((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) {
++              if (!opt->rr)
++                      return 0;
++      }
++      else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_RR) == IPT_IPV4OPTION_DONT_MATCH_RR) {
++              if (opt->rr)
++                      return 0;
++      }
++      /* timestamp */
++      if ((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) {
++              if (!opt->ts)
++                      return 0;
++      }
++      else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) == IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) {
++              if (opt->ts)
++                      return 0;
++      }
++      /* router-alert option  */
++      if ((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT) {
++              if (!opt->router_alert)
++                      return 0;
++      }
++      else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) {
++              if (opt->router_alert)
++                      return 0;
++      }
++
++      /* we match ! */
++      return 1;
++}
++
++static int
++checkentry(const char *tablename,
++         const struct ipt_ip *ip,
++         void *matchinfo,
++         unsigned int matchsize,
++         unsigned int hook_mask)
++{
++      const struct ipt_ipv4options_info *info = matchinfo;   /* match info for rule */
++      /* Check the size */
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_ipv4options_info)))
++              return 0;
++      /* Now check the coherence of the data ... */
++      if (((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT) &&
++          (((info->options & IPT_IPV4OPTION_DONT_MATCH_SRR) == IPT_IPV4OPTION_DONT_MATCH_SRR) ||
++           ((info->options & IPT_IPV4OPTION_DONT_MATCH_RR) == IPT_IPV4OPTION_DONT_MATCH_RR) ||
++           ((info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) == IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) ||
++           ((info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) ||
++           ((info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) == IPT_IPV4OPTION_DONT_MATCH_ANY_OPT)))
++              return 0; /* opposites */
++      if (((info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) == IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) &&
++          (((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR) ||
++           ((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) ||
++           ((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) ||
++           ((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) ||
++           ((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT) ||
++           ((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT)))
++              return 0; /* opposites */
++      if (((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) &&
++          ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR))
++              return 0; /* cannot match in the same time loose and strict source routing */
++      if ((((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) ||
++           ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR)) &&
++          ((info->options & IPT_IPV4OPTION_DONT_MATCH_SRR) == IPT_IPV4OPTION_DONT_MATCH_SRR))
++              return 0; /* opposites */
++      if (((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) &&
++          ((info->options & IPT_IPV4OPTION_DONT_MATCH_RR) == IPT_IPV4OPTION_DONT_MATCH_RR))
++              return 0; /* opposites */
++      if (((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) &&
++          ((info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) == IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP))
++              return 0; /* opposites */
++      if (((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT) &&
++          ((info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT))
++              return 0; /* opposites */
++
++      /* everything looks ok. */
++      return 1;
++}
++
++static struct ipt_match ipv4options_match
++= { { NULL, NULL }, "ipv4options", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      printk("ipt_ipv4options loading\n");
++      return ipt_register_match(&ipv4options_match);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&ipv4options_match);
++      printk("ipt_ipv4options unloaded\n");
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_mark.c linux-2.4.20/net/ipv4/netfilter/ipt_mark.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_mark.c     Sun Sep 30 19:26:08 2001
++++ linux-2.4.20/net/ipv4/netfilter/ipt_mark.c Wed Sep 24 09:18:10 2003
+@@ -15,9 +15,15 @@
+       u_int16_t datalen,
+       int *hotdrop)
+ {
+-      const struct ipt_mark_info *info = matchinfo;
++      const struct ipt_mark_info *info = (struct ipt_mark_info *)matchinfo;
+-      return ((skb->nfmark & info->mask) == info->mark) ^ info->invert;
++      if (info->bit_op == IPT_MARK_BIT_OP_NONE)
++              return (skb->nfmark == info->mark) ^ info->invert;
++      else
++              if (info->bit_op == IPT_MARK_BIT_OP_AND)
++                      return ((skb->nfmark & info->mask) == info->mark) ^ info->invert;
++              else
++                      return ((skb->nfmark | info->mask) == info->mark) ^ info->invert;
+ }
+ static int
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_mport.c linux-2.4.20/net/ipv4/netfilter/ipt_mport.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_mport.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_mport.c        Wed Sep 24 09:16:51 2003
+@@ -0,0 +1,112 @@
++/* Kernel module to match one of a list of TCP/UDP ports: ports are in
++   the same place so we can treat them as equal. */
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/udp.h>
++#include <linux/skbuff.h>
++
++#include <linux/netfilter_ipv4/ipt_mport.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++MODULE_LICENSE("GPL");
++
++#if 0
++#define duprintf(format, args...) printk(format , ## args)
++#else
++#define duprintf(format, args...)
++#endif
++
++/* Returns 1 if the port is matched by the test, 0 otherwise. */
++static inline int
++ports_match(const struct ipt_mport *minfo, u_int16_t src, u_int16_t dst)
++{
++      unsigned int i;
++        unsigned int m;
++        u_int16_t pflags = minfo->pflags;
++      for (i=0, m=1; i<IPT_MULTI_PORTS; i++, m<<=1) {
++                u_int16_t s, e;
++
++                if (pflags & m
++                    && minfo->ports[i] == 65535)
++                        return 0;
++
++                s = minfo->ports[i];
++
++                if (pflags & m) {
++                        e = minfo->ports[++i];
++                        m <<= 1;
++                } else
++                        e = s;
++
++                if (minfo->flags & IPT_MPORT_SOURCE
++                    && src >= s && src <= e)
++                        return 1;
++
++              if (minfo->flags & IPT_MPORT_DESTINATION
++                  && dst >= s && dst <= e)
++                      return 1;
++      }
++
++      return 0;
++}
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      const struct udphdr *udp = hdr;
++      const struct ipt_mport *minfo = matchinfo;
++
++      /* Must be big enough to read ports. */
++      if (offset == 0 && datalen < sizeof(struct udphdr)) {
++              /* We've been asked to examine this packet, and we
++                 can't.  Hence, no choice but to drop. */
++                      duprintf("ipt_mport:"
++                               " Dropping evil offset=0 tinygram.\n");
++                      *hotdrop = 1;
++                      return 0;
++      }
++
++      /* Must not be a fragment. */
++      return !offset
++              && ports_match(minfo, ntohs(udp->source), ntohs(udp->dest));
++}
++
++/* Called when user tries to insert an entry of this type. */
++static int
++checkentry(const char *tablename,
++         const struct ipt_ip *ip,
++         void *matchinfo,
++         unsigned int matchsize,
++         unsigned int hook_mask)
++{
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_mport)))
++              return 0;
++
++      /* Must specify proto == TCP/UDP, no unknown flags or bad count */
++      return (ip->proto == IPPROTO_TCP || ip->proto == IPPROTO_UDP)
++              && !(ip->invflags & IPT_INV_PROTO)
++              && matchsize == IPT_ALIGN(sizeof(struct ipt_mport));
++}
++
++static struct ipt_match mport_match
++= { { NULL, NULL }, "mport", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      return ipt_register_match(&mport_match);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&mport_match);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_multiport.c linux-2.4.20/net/ipv4/netfilter/ipt_multiport.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_multiport.c        Sun Sep 30 19:26:08 2001
++++ linux-2.4.20/net/ipv4/netfilter/ipt_multiport.c    Wed Sep 24 09:16:14 2003
+@@ -78,7 +78,7 @@
+       /* Must specify proto == TCP/UDP, no unknown flags or bad count */
+       return (ip->proto == IPPROTO_TCP || ip->proto == IPPROTO_UDP)
+-              && !(ip->flags & IPT_INV_PROTO)
++              && !(ip->invflags & IPT_INV_PROTO)
+               && matchsize == IPT_ALIGN(sizeof(struct ipt_multiport))
+               && (multiinfo->flags == IPT_MULTIPORT_SOURCE
+                   || multiinfo->flags == IPT_MULTIPORT_DESTINATION
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_nth.c linux-2.4.20/net/ipv4/netfilter/ipt_nth.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_nth.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_nth.c  Wed Sep 24 09:16:53 2003
+@@ -0,0 +1,172 @@
++/*
++  This is a module which is used for match support for every Nth packet
++  This file is distributed under the terms of the GNU General Public
++  License (GPL). Copies of the GPL can be obtained from:
++     ftp://prep.ai.mit.edu/pub/gnu/GPL
++
++  2001-07-18 Fabrice MARIE <fabrice@netfilter.org> : initial implementation.
++  2001-09-20 Richard Wagner (rwagner@cloudnet.com)
++        * added support for multiple counters
++        * added support for matching on individual packets
++          in the counter cycle
++
++*/
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <net/tcp.h>
++#include <linux/spinlock.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_nth.h>
++
++MODULE_LICENSE("GPL");
++
++/*
++ * State information.
++ */
++struct state {
++      spinlock_t lock;
++      u_int16_t number;
++};
++
++static struct state states[IPT_NTH_NUM_COUNTERS];
++
++static int
++ipt_nth_match(const struct sk_buff *pskb,
++            const struct net_device *in,
++            const struct net_device *out,
++            const void *matchinfo,
++            int offset,
++            const void *hdr,
++            u_int16_t datalen,
++            int *hotdrop)
++{
++      /* Parameters from userspace */
++      const struct ipt_nth_info *info = matchinfo;
++        unsigned counter = info->counter;
++              if((counter < 0) || (counter >= IPT_NTH_NUM_COUNTERS)) 
++              {
++                      printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IPT_NTH_NUM_COUNTERS-1);
++               return 0;
++        };
++
++        spin_lock(&states[counter].lock);
++
++        /* Are we matching every nth packet?*/
++        if (info->packet == 0xFF)
++        {
++              /* We're matching every nth packet and only every nth packet*/
++              /* Do we match or invert match? */
++              if (info->not == 0)
++              {
++                      if (states[counter].number == 0)
++                      {
++                              ++states[counter].number;
++                              goto match;
++                      }
++                      if (states[counter].number >= info->every)
++                              states[counter].number = 0; /* reset the counter */
++                      else
++                              ++states[counter].number;
++                      goto dontmatch;
++              }
++              else
++              {
++                      if (states[counter].number == 0)
++                      {
++                              ++states[counter].number;
++                              goto dontmatch;
++                      }
++                      if (states[counter].number >= info->every)
++                              states[counter].number = 0;
++                      else
++                              ++states[counter].number;
++                      goto match;
++              }
++        }
++        else
++        {
++              /* We're using the --packet, so there must be a rule for every value */
++              if (states[counter].number == info->packet)
++              {
++                      /* only increment the counter when a match happens */
++                      if (states[counter].number >= info->every)
++                              states[counter].number = 0; /* reset the counter */
++                      else
++                              ++states[counter].number;
++                      goto match;
++              }
++              else
++                      goto dontmatch;
++      }
++
++ dontmatch:
++      /* don't match */
++      spin_unlock(&states[counter].lock);
++      return 0;
++
++ match:
++      spin_unlock(&states[counter].lock);
++      return 1;
++}
++
++static int
++ipt_nth_checkentry(const char *tablename,
++                 const struct ipt_ip *e,
++                 void *matchinfo,
++                 unsigned int matchsize,
++                 unsigned int hook_mask)
++{
++      /* Parameters from userspace */
++      const struct ipt_nth_info *info = matchinfo;
++        unsigned counter = info->counter;
++        if((counter < 0) || (counter >= IPT_NTH_NUM_COUNTERS)) 
++      {
++              printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IPT_NTH_NUM_COUNTERS-1);
++                      return 0;
++              };
++
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_nth_info))) {
++              printk("nth: matchsize %u != %u\n", matchsize,
++                     IPT_ALIGN(sizeof(struct ipt_nth_info)));
++              return 0;
++      }
++
++      states[counter].number = info->startat;
++
++      return 1;
++}
++
++static struct ipt_match ipt_nth_reg = { 
++      {NULL, NULL},
++      "nth",
++      ipt_nth_match,
++      ipt_nth_checkentry,
++      NULL,
++      THIS_MODULE };
++
++static int __init init(void)
++{
++      unsigned counter;
++        memset(&states, 0, sizeof(states));
++      if (ipt_register_match(&ipt_nth_reg))
++              return -EINVAL;
++
++        for(counter = 0; counter < IPT_NTH_NUM_COUNTERS; counter++) 
++      {
++              spin_lock_init(&(states[counter].lock));
++        };
++
++      printk("ipt_nth match loaded\n");
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&ipt_nth_reg);
++      printk("ipt_nth match unloaded\n");
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_osf.c linux-2.4.20/net/ipv4/netfilter/ipt_osf.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_osf.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_osf.c  Wed Sep 24 09:16:57 2003
+@@ -0,0 +1,685 @@
++/*
++ * ipt_osf.c
++ *
++ * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/*
++ * OS fingerprint matching module.
++ * It simply compares various parameters from SYN packet with
++ * some hardcoded ones.
++ *
++ * Original table was created by Michal Zalewski <lcamtuf@coredump.cx>
++ * for his p0f.
++ */
++
++#include <linux/smp.h>
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/file.h>
++#include <linux/ip.h>
++#include <linux/proc_fs.h>
++#include <linux/fs.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/ctype.h>
++#include <linux/list.h>
++
++#include <net/sock.h>
++#include <net/ip.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++#include <linux/netfilter_ipv4/ipt_osf.h>
++
++//#define OSF_DEBUG
++
++#ifdef OSF_DEBUG
++#define log(x...)             printk(KERN_ERR "ipt_osf: " x)
++#define loga(x...)            printk(x)
++#else
++#define log(x...)             do {} while(0)
++#define loga(x...)            do {} while(0)
++#endif
++
++#define FMATCH_WRONG          0
++#define FMATCH_OK             1
++#define FMATCH_OPT_WRONG      2
++
++#define OPTDEL                        ','
++#define OSFPDEL               ':'
++#define MAXOPTSTRLEN          128
++#define OSFFLUSH              "FLUSH"
++
++static rwlock_t osf_lock = RW_LOCK_UNLOCKED;
++static struct list_head       finger_list;    
++
++static int match(const struct sk_buff *, const struct net_device *, const struct net_device *,
++                    const void *, int, const void *, u_int16_t, int *);
++static int checkentry(const char *, const struct ipt_ip *, void *,
++                         unsigned int, unsigned int);
++
++static struct ipt_match osf_match = 
++{ 
++      { NULL, NULL }, 
++      "osf", 
++      &match, 
++      &checkentry, 
++      NULL, 
++      THIS_MODULE 
++};
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      struct ipt_osf_info *info = (struct ipt_osf_info *)matchinfo;
++      struct iphdr *ip = skb->nh.iph;
++      struct tcphdr *tcp;
++      int fmatch = FMATCH_WRONG;
++      unsigned long totlen, optsize = 0, window;
++      unsigned char df, *optp = NULL, *_optp = NULL;
++      char check_WSS = 0;
++      struct list_head *ent;
++      struct osf_finger *f;
++
++      if (!ip || !info)
++              return 0;
++                              
++      tcp = (struct tcphdr *)((u_int32_t *)ip + ip->ihl);
++
++      if (!tcp->syn)
++              return 0;
++      
++      totlen = ntohs(ip->tot_len);
++      df = ((ntohs(ip->frag_off) & IP_DF)?1:0);
++      window = ntohs(tcp->window);
++      
++      if (tcp->doff*4 > sizeof(struct tcphdr))
++      {
++              _optp = optp = (char *)(tcp+1);
++              optsize = tcp->doff*4 - sizeof(struct tcphdr);
++      }
++
++      
++      /* Actually we can create hash/table of all genres and search
++       * only in appropriate part, but here is initial variant,
++       * so will use slow path.
++       */
++      read_lock(&osf_lock);
++      list_for_each(ent, &finger_list)
++      {
++              f = list_entry(ent, struct osf_finger, flist);
++              
++              if (strcmp(info->genre, f->genre)) 
++                      continue;
++
++              optp = _optp;
++
++              if (    ip->ttl == f->ttl &&
++                      totlen  == f->ss &&
++                      df      == f->df)
++              {
++                      unsigned long foptsize;
++                      int optnum;
++                      unsigned short mss = 0;
++
++                      check_WSS = 0;
++
++                      switch (f->wss.wc)
++                      {
++                              case 0:   check_WSS = 0; break;
++                              case 'S': check_WSS = 1; break;
++                              case 'T': check_WSS = 2; break;
++                              case '%': check_WSS = 3; break;
++                              default: log("Wrong fingerprint wss.wc=%d, %s - %s\n", 
++                                                       f->wss.wc, f->genre, f->details);
++                                       check_WSS = 4;
++                                       break;
++                      }
++                      if (check_WSS == 4)
++                              continue;
++
++                      /* Check options */
++
++                      foptsize = 0;
++                      for (optnum=0; optnum<f->opt_num; ++optnum)
++                              foptsize += f->opt[optnum].length;
++
++                              
++                      if (foptsize > MAX_IPOPTLEN || optsize > MAX_IPOPTLEN || optsize != foptsize)
++                              continue;
++
++                      if (!optp)
++                      {
++                              fmatch = FMATCH_OK;
++                              loga("\tYEP : matching without options.\n");
++                              break;
++                      }
++                      
++
++                      for (optnum=0; optnum<f->opt_num; ++optnum)
++                      {
++                              if (f->opt[optnum].kind == (*optp))
++                              {
++                                      unsigned char len = f->opt[optnum].length;
++                                      unsigned char *optend = optp + len;
++
++                                      fmatch = FMATCH_OK;
++
++                                      if (*optp == OSFOPT_MSS) /* MSS */
++                                              mss = ntohs(*(unsigned short *)(optp+2));
++                                      
++                                      if (len != 1)
++                                      {
++                                              /* Skip kind and length fields*/
++                                              optp += 2; 
++
++                                              if (f->opt[optnum].wc.wc != 0)
++                                              {
++                                                      unsigned long tmp = 0;
++                                                      
++                                                      /* Hmmm... It looks a bit ugly. :) */
++                                                      memcpy(&tmp, &f->opt[optnum].wc.val, 
++                                                              (len > sizeof(unsigned long)?
++                                                                      sizeof(unsigned long):len));
++
++                                                      tmp = ntohl(tmp);
++                                                      if (tmp != f->opt[optnum].wc.val)
++                                                              fmatch = FMATCH_OPT_WRONG;
++                                              }
++                                      }
++
++                                      optp = optend;
++                              }
++                              else
++                                      fmatch = FMATCH_OPT_WRONG;
++
++                              if (fmatch != FMATCH_OK)
++                                      break;
++                      }
++
++                      if (fmatch != FMATCH_OPT_WRONG)
++                      {
++                              fmatch = FMATCH_WRONG;
++
++                              switch (check_WSS)
++                              {
++                                      case 0:
++                                              if (window == f->wss.val)
++                                                      fmatch = FMATCH_OK;
++                                              break;
++                                      case 1: /* MSS */
++                                              if (window == f->wss.val*mss)
++                                                      fmatch = FMATCH_OK;
++                                              break;
++                                      case 2: /* MTU */
++                                              if (window == f->wss.val*(mss+40))
++                                                      fmatch = FMATCH_OK;
++                                              break;
++                                      case 3: /* MOD */
++                                              if (window % f->wss.val == 0)
++                                                      fmatch = FMATCH_OK;
++                                              break;
++                              }
++                      }
++                                      
++
++                      if (fmatch == FMATCH_OK)
++                      {
++
++                              log("genre %s[%25s]: ttl=%d, size=%lu, df=%d, "
++                                      "check_WSS=%d, win=%lu, optsize=%lu:\n", 
++                                      f->genre, f->details, 
++                                      ip->ttl, totlen, df, 
++                                      check_WSS, window, optsize);
++                              break;
++                      }
++              }
++      }
++      read_unlock(&osf_lock);
++
++      return (fmatch == FMATCH_OK)?1:0;
++}
++
++static int
++checkentry(const char *tablename,
++           const struct ipt_ip *ip,
++           void *matchinfo,
++           unsigned int matchsize,
++           unsigned int hook_mask)
++{
++       if (matchsize != IPT_ALIGN(sizeof(struct ipt_osf_info)))
++               return 0;
++       if (ip->proto != IPPROTO_TCP)
++             return 0;
++
++       return 1;
++}
++
++static struct osf_finger * finger_alloc()
++{
++      struct osf_finger *f;
++
++      f = kmalloc(sizeof(struct osf_finger), GFP_KERNEL);
++      if (f)
++              memset(f, 0, sizeof(struct osf_finger));
++      
++      return f;
++}
++
++static void finger_free(struct osf_finger *f)
++{
++      memset(f, 0, sizeof(struct osf_finger));
++      kfree(f);
++}
++
++
++static void osf_parse_opt(struct osf_opt *opt, int *optnum, char *obuf, int olen)
++{
++      int i, op;
++      char *ptr, wc;
++      unsigned long val;
++
++      ptr = &obuf[0];
++      i = 0;
++      while (ptr != NULL && i < olen)
++      {
++              val = 0;
++              op = 0;
++              wc = 0;
++              switch (obuf[i])
++              {
++                      case 'N': 
++                              op = OSFOPT_NOP;
++                              ptr = strchr(&obuf[i], OPTDEL);
++                              if (ptr)
++                              {
++                                      *ptr = '\0';
++                                      ptr++;
++                                      i += (int)(ptr-&obuf[i]);
++
++                              }
++                              else
++                                      i++;
++                              break;
++                      case 'S': 
++                              op = OSFOPT_SACKP;
++                              ptr = strchr(&obuf[i], OPTDEL);
++                              if (ptr)
++                              {
++                                      *ptr = '\0';
++                                      ptr++;
++                                      i += (int)(ptr-&obuf[i]);
++
++                              }
++                              else
++                                      i++;
++                              break;
++                      case 'T': 
++                              op = OSFOPT_TS;
++                              ptr = strchr(&obuf[i], OPTDEL);
++                              if (ptr)
++                              {
++                                      *ptr = '\0';
++                                      ptr++;
++                                      i += (int)(ptr-&obuf[i]);
++
++                              }
++                              else
++                                      i++;
++                              break;
++                      case 'W': 
++                              op = OSFOPT_WSO;
++                              ptr = strchr(&obuf[i], OPTDEL);
++                              if (ptr)
++                              {
++                                      switch (obuf[i+1])
++                                      {
++                                              case '%':       wc = '%'; break;
++                                              case 'S':       wc = 'S'; break;
++                                              case 'T':       wc = 'T'; break;
++                                              default:        wc = 0; break;
++                                      }
++                                      
++                                      *ptr = '\0';
++                                      ptr++;
++                                      if (wc)
++                                              val = simple_strtoul(&obuf[i+2], NULL, 10);
++                                      else
++                                              val = simple_strtoul(&obuf[i+1], NULL, 10);
++                                      i += (int)(ptr-&obuf[i]);
++
++                              }
++                              else
++                                      i++;
++                              break;
++                      case 'M': 
++                              op = OSFOPT_MSS;
++                              ptr = strchr(&obuf[i], OPTDEL);
++                              if (ptr)
++                              {
++                                      if (obuf[i+1] == '%')
++                                              wc = '%';
++                                      *ptr = '\0';
++                                      ptr++;
++                                      if (wc)
++                                              val = simple_strtoul(&obuf[i+2], NULL, 10);
++                                      else
++                                              val = simple_strtoul(&obuf[i+1], NULL, 10);
++                                      i += (int)(ptr-&obuf[i]);
++
++                              }
++                              else
++                                      i++;
++                              break;
++                      case 'E': 
++                              op = OSFOPT_EOL;
++                              ptr = strchr(&obuf[i], OPTDEL);
++                              if (ptr)
++                              {
++                                      *ptr = '\0';
++                                      ptr++;
++                                      i += (int)(ptr-&obuf[i]);
++
++                              }
++                              else
++                                      i++;
++                              break;
++                      default:
++                              ptr = strchr(&obuf[i], OPTDEL);
++                              if (ptr)
++                              {
++                                      ptr++;
++                                      i += (int)(ptr-&obuf[i]);
++
++                              }
++                              else
++                                      i++;
++                              break;
++              }
++
++              opt[*optnum].kind       = IANA_opts[op].kind;
++              opt[*optnum].length     = IANA_opts[op].length;
++              opt[*optnum].wc.wc      = wc;
++              opt[*optnum].wc.val     = val;
++
++              log("opt %2d: kind=%2d, length=%2d, wc.wc=%d, wc.val=%5lu\n",
++                      *optnum, opt[*optnum].kind, opt[*optnum].length, 
++                      opt[*optnum].wc.wc, opt[*optnum].wc.val);
++              
++              (*optnum)++;
++      }
++}
++
++static int osf_proc_read(char *buf, char **start, off_t off, int count, int *eof, void *data)
++{
++      struct list_head *ent;
++      struct osf_finger *f = NULL;
++      int i;
++      
++      *eof = 1;
++      count = 0;
++
++      read_lock_bh(&osf_lock);
++      list_for_each(ent, &finger_list)
++      {
++              f = list_entry(ent, struct osf_finger, flist);
++
++              log("%s - %s: ttl=%d, wss=%c%lu, df=%d, size=%lu",
++                      f->genre, f->details, f->ttl, (f->wss.wc)?f->wss.wc:' ', f->wss.val, f->df, f->ss);
++              
++              count += sprintf(buf+count, "%s - %s: ttl=%d, wss=%c%lu, df=%d, size=%lu",
++                      f->genre, f->details, f->ttl, (f->wss.wc)?f->wss.wc:' ', f->wss.val, f->df, f->ss);
++              
++              if (f->opt_num)
++              {
++                      loga(" OPT: ");
++                      //count += sprintf(buf+count, " OPT: ");
++                      for (i=0; i<f->opt_num; ++i)
++                      {
++                              //count += sprintf(buf+count, "%d.%c%lu; ", 
++                              //      f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', f->opt[i].wc.val);
++                              loga("%d.%c%lu; ", 
++                                      f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', f->opt[i].wc.val);
++                      }
++              }
++              loga("\n");
++              count += sprintf(buf+count, "\n");
++      }
++      read_unlock_bh(&osf_lock);
++
++      return count;
++}
++
++static int osf_proc_write(struct file *file, const char *buffer, unsigned long count, void *data)
++{
++      int cnt;
++      unsigned long i;
++      char quirks[MAXOPTSTRLEN], obuf[MAXOPTSTRLEN];
++      struct osf_finger *finger;
++      struct list_head *ent, *n;
++
++      char *pbeg, *pend;
++
++      if (count == strlen(OSFFLUSH) && !strncmp(buffer, OSFFLUSH, strlen(OSFFLUSH)))
++      {
++              int i = 0;
++              write_lock_bh(&osf_lock);
++              list_for_each_safe(ent, n, &finger_list)
++              {
++                      i++;
++                      finger = list_entry(ent, struct osf_finger, flist);
++                      list_del(&finger->flist);
++                      finger_free(finger);
++              }
++              write_unlock_bh(&osf_lock);
++      
++              log("Flushed %d entries.\n", i);
++              
++              return count;
++      }
++
++      
++      cnt = 0;
++      for (i=0; i<count && buffer[i] != '\0'; ++i)
++              if (buffer[i] == ':')
++                      cnt++;
++
++      if (cnt != 7 || i != count)
++      {
++              log("Wrong input line cnt=%d[7], len=%lu[%lu]\n", 
++                      cnt, i, count);
++              return count;
++      }
++
++
++      memset(quirks, 0, sizeof(quirks));
++      memset(obuf, 0, sizeof(obuf));
++      
++      finger = finger_alloc();
++      if (!finger)
++      {
++              log("Failed to allocate new fingerprint entry.\n");
++              return -ENOMEM;
++      }
++
++      pbeg = (char *)buffer;
++      pend = strchr(pbeg, OSFPDEL);
++      if (pend)
++      {
++              *pend = '\0';
++              if (pbeg[0] == 'S')
++              {
++                      finger->wss.wc = 'S';
++                      if (pbeg[1] == '%')
++                              finger->wss.val = simple_strtoul(pbeg+2, NULL, 10);
++                      else if (pbeg[1] == '*')
++                              finger->wss.val = 0;
++                      else 
++                              finger->wss.val = simple_strtoul(pbeg+1, NULL, 10);
++              }
++              else if (pbeg[0] == 'T')
++              {
++                      finger->wss.wc = 'T';
++                      if (pbeg[1] == '%')
++                              finger->wss.val = simple_strtoul(pbeg+2, NULL, 10);
++                      else if (pbeg[1] == '*')
++                              finger->wss.val = 0;
++                      else 
++                              finger->wss.val = simple_strtoul(pbeg+1, NULL, 10);
++              }
++              if (isdigit(pbeg[0]))
++              {
++                      finger->wss.wc = 0;
++                      finger->wss.val = simple_strtoul(pbeg, NULL, 10);
++              }
++
++              pbeg = pend+1;
++      }
++      pend = strchr(pbeg, OSFPDEL);
++      if (pend)
++      {
++              *pend = '\0';
++              finger->ttl = simple_strtoul(pbeg, NULL, 10);
++              pbeg = pend+1;
++      }
++      pend = strchr(pbeg, OSFPDEL);
++      if (pend)
++      {
++              *pend = '\0';
++              finger->df = simple_strtoul(pbeg, NULL, 10);
++              pbeg = pend+1;
++      }
++      pend = strchr(pbeg, OSFPDEL);
++      if (pend)
++      {
++              *pend = '\0';
++              finger->ss = simple_strtoul(pbeg, NULL, 10);
++              pbeg = pend+1;
++      }
++
++      pend = strchr(pbeg, OSFPDEL);
++      if (pend)
++      {
++              *pend = '\0';
++              cnt = snprintf(obuf, sizeof(obuf), "%s", pbeg);
++              pbeg = pend+1;
++      }
++      
++      pend = strchr(pbeg, OSFPDEL);
++      if (pend)
++      {
++              *pend = '\0';
++              cnt = snprintf(quirks, sizeof(quirks), "%s", pbeg);
++              pbeg = pend+1;
++      }
++
++      pend = strchr(pbeg, OSFPDEL);
++      if (pend)
++      {
++              *pend = '\0';
++              if (pbeg[0] == '@' || pbeg[0] == '*')
++                      cnt = snprintf(finger->genre, 
++                                      ((count-(pbeg+1-buffer)+1) > MAXGENRELEN)?MAXGENRELEN:(count-(pbeg+1-buffer)+1), 
++                                      "%s", pbeg+1);
++              else
++                      cnt = snprintf(finger->genre, 
++                                      ((count-(pbeg-buffer)+1) > MAXGENRELEN)?MAXGENRELEN:(count-(pbeg-buffer)+1), 
++                                      "%s", pbeg);
++              pbeg = pend+1;
++      }
++
++      cnt = snprintf(finger->details, 
++                      ((count - (pbeg - buffer)+1) > MAXDETLEN)?MAXDETLEN:(count - (pbeg - buffer)+1), 
++                      "%s", pbeg);
++      
++      log("%s - %s. ttl=%d, df=%d, size=%lu, wss=[%d, %lu]\n", 
++              finger->genre, finger->details,
++              finger->ttl, finger->df, finger->ss,
++              finger->wss.wc, finger->wss.val);
++      
++      osf_parse_opt(finger->opt, &finger->opt_num, obuf, sizeof(obuf));
++      
++
++      write_lock_bh(&osf_lock);
++      list_add(&finger->flist, &finger_list);
++      write_unlock_bh(&osf_lock);
++
++      return count;
++}
++
++static int __init osf_init(void)
++{
++      int err;
++      struct proc_dir_entry *p;
++
++      log("Startng OS fingerprint matching module.\n");
++
++      INIT_LIST_HEAD(&finger_list);
++      
++      err = ipt_register_match(&osf_match);
++      if (err)
++      {
++              log("Failed to register OS fingerprint matching module.\n");
++              return -ENXIO;
++      }
++
++      p = create_proc_entry("sys/net/ipv4/osf", S_IFREG | 0644, NULL);
++      if (!p)
++      {
++              ipt_unregister_match(&osf_match);
++              return -ENXIO;
++      }
++
++      p->write_proc = osf_proc_write;
++      p->read_proc  = osf_proc_read;
++
++      return 0;
++}
++
++static void __exit osf_fini(void)
++{
++      struct list_head *ent, *n;
++      struct osf_finger *f;
++      
++      remove_proc_entry("sys/net/ipv4/osf", NULL);
++      ipt_unregister_match(&osf_match);
++
++      list_for_each_safe(ent, n, &finger_list)
++      {
++              f = list_entry(ent, struct osf_finger, flist);
++              list_del(&f->flist);
++              finger_free(f);
++      }
++      
++      log("OS fingerprint matching module finished.\n");
++}
++
++module_init(osf_init);
++module_exit(osf_fini);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
++MODULE_DESCRIPTION("Passive OS fingerprint matching.");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_owner.c linux-2.4.20/net/ipv4/netfilter/ipt_owner.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_owner.c    Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ipt_owner.c        Wed Sep 24 09:17:52 2003
+@@ -2,17 +2,25 @@
+    locally generated outgoing packets.
+    Copyright (C) 2000 Marc Boucher
++
++   03/26/2003 Patrick McHardy <kaber@trash.net>: LOCAL_IN support
+  */
+ #include <linux/module.h>
+ #include <linux/skbuff.h>
+ #include <linux/file.h>
++#include <linux/ip.h>
++#include <linux/tcp.h>
++#include <linux/udp.h>
+ #include <net/sock.h>
++#include <net/tcp.h>
++#include <net/udp.h>
++#include <net/route.h>
+ #include <linux/netfilter_ipv4/ipt_owner.h>
+ #include <linux/netfilter_ipv4/ip_tables.h>
+ static int
+-match_comm(const struct sk_buff *skb, const char *comm)
++match_comm(const struct sock *sk, const char *comm)
+ {
+       struct task_struct *p;
+       struct files_struct *files;
+@@ -28,7 +36,7 @@
+               if(files) {
+                       read_lock(&files->file_lock);
+                       for (i=0; i < files->max_fds; i++) {
+-                              if (fcheck_files(files, i) == skb->sk->socket->file) {
++                              if (fcheck_files(files, i) == sk->socket->file) {
+                                       read_unlock(&files->file_lock);
+                                       task_unlock(p);
+                                       read_unlock(&tasklist_lock);
+@@ -44,7 +52,7 @@
+ }
+ static int
+-match_pid(const struct sk_buff *skb, pid_t pid)
++match_pid(const struct sock *sk, pid_t pid)
+ {
+       struct task_struct *p;
+       struct files_struct *files;
+@@ -59,7 +67,7 @@
+       if(files) {
+               read_lock(&files->file_lock);
+               for (i=0; i < files->max_fds; i++) {
+-                      if (fcheck_files(files, i) == skb->sk->socket->file) {
++                      if (fcheck_files(files, i) == sk->socket->file) {
+                               read_unlock(&files->file_lock);
+                               task_unlock(p);
+                               read_unlock(&tasklist_lock);
+@@ -75,10 +83,10 @@
+ }
+ static int
+-match_sid(const struct sk_buff *skb, pid_t sid)
++match_sid(const struct sock *sk, pid_t sid)
+ {
+       struct task_struct *p;
+-      struct file *file = skb->sk->socket->file;
++      struct file *file = sk->socket->file;
+       int i, found=0;
+       read_lock(&tasklist_lock);
+@@ -119,41 +127,71 @@
+       int *hotdrop)
+ {
+       const struct ipt_owner_info *info = matchinfo;
+-
+-      if (!skb->sk || !skb->sk->socket || !skb->sk->socket->file)
+-              return 0;
++      struct iphdr *iph = skb->nh.iph;
++      struct sock *sk = NULL;
++      int ret = 0;
++
++      if (out) {
++              sk = skb->sk;
++      } else {
++              if (iph->protocol == IPPROTO_TCP) {
++                      struct tcphdr *tcph =
++                              (struct tcphdr*)((u_int32_t*)iph + iph->ihl);
++                      sk = tcp_v4_lookup(iph->saddr, tcph->source,
++                                         iph->daddr, tcph->dest,
++                                         ((struct rtable*)skb->dst)->rt_iif);
++                      if (sk && sk->state == TCP_TIME_WAIT) {
++                              tcp_tw_put((struct tcp_tw_bucket *)sk);
++                              return ret;
++                      }
++              } else if (iph->protocol == IPPROTO_UDP) {
++                      struct udphdr *udph =
++                              (struct udphdr*)((u_int32_t*)iph + iph->ihl);
++                      sk = udp_v4_lookup(iph->saddr, udph->source, iph->daddr,
++                                         udph->dest, skb->dev->ifindex);
++              }
++      } 
++                                      
++      if (!sk || !sk->socket || !sk->socket->file)
++              goto out;
+       if(info->match & IPT_OWNER_UID) {
+-              if((skb->sk->socket->file->f_uid != info->uid) ^
++              if((sk->socket->file->f_uid != info->uid) ^
+                   !!(info->invert & IPT_OWNER_UID))
+-                      return 0;
++                      goto out;
+       }
+       if(info->match & IPT_OWNER_GID) {
+-              if((skb->sk->socket->file->f_gid != info->gid) ^
++              if((sk->socket->file->f_gid != info->gid) ^
+                   !!(info->invert & IPT_OWNER_GID))
+-                      return 0;
++                      goto out;
+       }
+       if(info->match & IPT_OWNER_PID) {
+-              if (!match_pid(skb, info->pid) ^
++              if (!match_pid(sk, info->pid) ^
+                   !!(info->invert & IPT_OWNER_PID))
+-                      return 0;
++                      goto out;
+       }
+       if(info->match & IPT_OWNER_SID) {
+-              if (!match_sid(skb, info->sid) ^
++              if (!match_sid(sk, info->sid) ^
+                   !!(info->invert & IPT_OWNER_SID))
+-                      return 0;
++                      goto out;
+       }
+       if(info->match & IPT_OWNER_COMM) {
+-              if (!match_comm(skb, info->comm) ^
++              if (!match_comm(sk, info->comm) ^
+                   !!(info->invert & IPT_OWNER_COMM))
+-                      return 0;
++                      goto out;
+       }
+-      return 1;
++      ret = 1;
++
++out:
++      if (in && sk)
++              sock_put(sk);
++
++      return ret;
+ }
+ static int
+@@ -164,11 +202,19 @@
+            unsigned int hook_mask)
+ {
+         if (hook_mask
+-            & ~((1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_POST_ROUTING))) {
+-                printk("ipt_owner: only valid for LOCAL_OUT or POST_ROUTING.\n");
++            & ~((1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_POST_ROUTING) |
++              (1 << NF_IP_LOCAL_IN))) {
++              printk("ipt_owner: only valid for LOCAL_IN, LOCAL_OUT "
++                     "or POST_ROUTING.\n");
+                 return 0;
+         }
++      if ((hook_mask & (1 << NF_IP_LOCAL_IN))
++          && ip->proto != IPPROTO_TCP && ip->proto != IPPROTO_UDP) {
++              printk("ipt_owner: only TCP or UDP can be used in LOCAL_IN\n");
++              return 0;
++      }
++
+       if (matchsize != IPT_ALIGN(sizeof(struct ipt_owner_info)))
+               return 0;
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_pool.c linux-2.4.20/net/ipv4/netfilter/ipt_pool.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_pool.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_pool.c Wed Sep 24 09:16:59 2003
+@@ -0,0 +1,71 @@
++/* Kernel module to match an IP address pool. */
++
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/skbuff.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_pool.h>
++#include <linux/netfilter_ipv4/ipt_pool.h>
++
++static inline int match_pool(
++      ip_pool_t index,
++      __u32 addr,
++      int inv
++) {
++      if (ip_pool_match(index, ntohl(addr)))
++              inv = !inv;
++      return inv;
++}
++
++static int match(
++      const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop
++) {
++      const struct ipt_pool_info *info = matchinfo;
++      const struct iphdr *iph = skb->nh.iph;
++
++      if (info->src != IP_POOL_NONE && !match_pool(info->src, iph->saddr,
++                                              info->flags&IPT_POOL_INV_SRC))
++              return 0;
++
++      if (info->dst != IP_POOL_NONE && !match_pool(info->dst, iph->daddr,
++                                              info->flags&IPT_POOL_INV_DST))
++              return 0;
++
++      return 1;
++}
++
++static int checkentry(
++      const char *tablename,
++      const struct ipt_ip *ip,
++      void *matchinfo,
++      unsigned int matchsize,
++      unsigned int hook_mask
++) {
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_pool_info)))
++              return 0;
++      return 1;
++}
++
++static struct ipt_match pool_match
++= { { NULL, NULL }, "pool", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      return ipt_register_match(&pool_match);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&pool_match);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_psd.c linux-2.4.20/net/ipv4/netfilter/ipt_psd.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_psd.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_psd.c  Wed Sep 24 09:17:01 2003
+@@ -0,0 +1,361 @@
++/*
++  This is a module which is used for PSD (portscan detection)
++  Derived from scanlogd v2.1 written by Solar Designer <solar@false.com>
++  and LOG target module.
++
++  Copyright (C) 2000,2001 astaro AG
++
++  This file is distributed under the terms of the GNU General Public
++  License (GPL). Copies of the GPL can be obtained from:
++     ftp://prep.ai.mit.edu/pub/gnu/GPL
++
++  2000-05-04 Markus Hennig <hennig@astaro.de> : initial
++  2000-08-18 Dennis Koslowski <koslowski@astaro.de> : first release
++  2000-12-01 Dennis Koslowski <koslowski@astaro.de> : UDP scans detection added
++  2001-01-02 Dennis Koslowski <koslowski@astaro.de> : output modified
++  2001-02-04 Jan Rekorajski <baggins@pld.org.pl> : converted from target to match
++*/
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <net/tcp.h>
++#include <linux/spinlock.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_psd.h>
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Dennis Koslowski <koslowski@astaro.com>");
++
++#define HF_DADDR_CHANGING   0x01
++#define HF_SPORT_CHANGING   0x02
++#define HF_TOS_CHANGING           0x04
++#define HF_TTL_CHANGING           0x08
++            
++/*
++ * Information we keep per each target port
++ */
++struct port {
++      u_int16_t number;      /* port number */ 
++      u_int8_t proto;        /* protocol number */
++      u_int8_t and_flags;    /* tcp ANDed flags */
++      u_int8_t or_flags;     /* tcp ORed flags */
++};
++
++/*
++ * Information we keep per each source address.
++ */
++struct host {
++      struct host *next;              /* Next entry with the same hash */
++      clock_t timestamp;              /* Last update time */
++      struct in_addr src_addr;        /* Source address */
++      struct in_addr dest_addr;       /* Destination address */
++      unsigned short src_port;        /* Source port */
++      int count;                      /* Number of ports in the list */
++      int weight;                     /* Total weight of ports in the list */
++      struct port ports[SCAN_MAX_COUNT - 1];  /* List of ports */
++      unsigned char tos;              /* TOS */
++      unsigned char ttl;              /* TTL */
++      unsigned char flags;            /* HF_ flags bitmask */
++};
++
++/*
++ * State information.
++ */
++static struct {
++      spinlock_t lock;
++      struct host list[LIST_SIZE];    /* List of source addresses */
++      struct host *hash[HASH_SIZE];   /* Hash: pointers into the list */
++      int index;                      /* Oldest entry to be replaced */
++} state;
++
++/*
++ * Convert an IP address into a hash table index.
++ */
++static inline int hashfunc(struct in_addr addr)
++{
++      unsigned int value;
++      int hash;
++
++      value = addr.s_addr;
++      hash = 0;
++      do {
++              hash ^= value;
++      } while ((value >>= HASH_LOG));
++
++      return hash & (HASH_SIZE - 1);
++}
++
++static int
++ipt_psd_match(const struct sk_buff *pskb,
++            const struct net_device *in,
++            const struct net_device *out,
++            const void *matchinfo,
++            int offset,
++            const void *hdr,
++            u_int16_t datalen,
++            int *hotdrop)
++{
++      struct iphdr *ip_hdr;
++      struct tcphdr *tcp_hdr;
++      struct in_addr addr;
++      u_int16_t src_port,dest_port;
++      u_int8_t tcp_flags, proto;
++      clock_t now;
++      struct host *curr, *last, **head;
++      int hash, index, count;
++
++      /* Parameters from userspace */
++      const struct ipt_psd_info *psdinfo = matchinfo;
++
++      /* IP header */
++      ip_hdr = pskb->nh.iph;
++
++      /* Sanity check */
++      if (ntohs(ip_hdr->frag_off) & IP_OFFSET) {
++              DEBUGP("PSD: sanity check failed\n");
++              return 0;
++      }
++
++      /* TCP or UDP ? */
++      proto = ip_hdr->protocol;
++
++      if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
++              DEBUGP("PSD: protocol not supported\n");
++              return 0;
++      }
++
++      /* Get the source address, source & destination ports, and TCP flags */
++
++      addr.s_addr = ip_hdr->saddr;
++
++      tcp_hdr = (struct tcphdr*)((u_int32_t *)ip_hdr + ip_hdr->ihl);
++
++      /* Yep, it´s dirty */
++      src_port = tcp_hdr->source;
++      dest_port = tcp_hdr->dest;
++
++      if (proto == IPPROTO_TCP) {
++              tcp_flags = *((u_int8_t*)tcp_hdr + 13);
++      }
++      else {
++              tcp_flags = 0x00;
++      }
++
++      /* We're using IP address 0.0.0.0 for a special purpose here, so don't let
++       * them spoof us. [DHCP needs this feature - HW] */
++      if (!addr.s_addr) {
++              DEBUGP("PSD: spoofed source address (0.0.0.0)\n");
++              return 0;
++      }
++
++      /* Use jiffies here not to depend on someone setting the time while we're
++       * running; we need to be careful with possible return value overflows. */
++      now = jiffies;
++
++      spin_lock(&state.lock);
++
++      /* Do we know this source address already? */
++      count = 0;
++      last = NULL;
++      if ((curr = *(head = &state.hash[hash = hashfunc(addr)])))
++              do {
++                      if (curr->src_addr.s_addr == addr.s_addr) break;
++                      count++;
++                      if (curr->next) last = curr;
++              } while ((curr = curr->next));
++
++      if (curr) {
++
++              /* We know this address, and the entry isn't too old. Update it. */
++              if (now - curr->timestamp <= (psdinfo->delay_threshold*HZ)/100 &&
++                  time_after_eq(now, curr->timestamp)) {
++
++                      /* Just update the appropriate list entry if we've seen this port already */
++                      for (index = 0; index < curr->count; index++) {
++                              if (curr->ports[index].number == dest_port) {
++                                      curr->ports[index].proto = proto;
++                                      curr->ports[index].and_flags &= tcp_flags;
++                                      curr->ports[index].or_flags |= tcp_flags;
++                                      goto out_no_match;
++                              }
++                      }
++
++                      /* TCP/ACK and/or TCP/RST to a new port? This could be an outgoing connection. */
++                      if (proto == IPPROTO_TCP && (tcp_hdr->ack || tcp_hdr->rst))
++                              goto out_no_match;
++
++                      /* Packet to a new port, and not TCP/ACK: update the timestamp */
++                      curr->timestamp = now;
++
++                      /* Logged this scan already? Then drop the packet. */
++                      if (curr->weight >= psdinfo->weight_threshold)
++                              goto out_match;
++
++                      /* Specify if destination address, source port, TOS or TTL are not fixed */
++                      if (curr->dest_addr.s_addr != ip_hdr->daddr)
++                              curr->flags |= HF_DADDR_CHANGING;
++                      if (curr->src_port != src_port)
++                              curr->flags |= HF_SPORT_CHANGING;
++                      if (curr->tos != ip_hdr->tos)
++                              curr->flags |= HF_TOS_CHANGING;
++                      if (curr->ttl != ip_hdr->ttl)
++                              curr->flags |= HF_TTL_CHANGING;
++
++                      /* Update the total weight */
++                      curr->weight += (ntohs(dest_port) < 1024) ?
++                              psdinfo->lo_ports_weight : psdinfo->hi_ports_weight;
++
++                      /* Got enough destination ports to decide that this is a scan? */
++                      /* Then log it and drop the packet. */
++                      if (curr->weight >= psdinfo->weight_threshold)
++                              goto out_match;
++
++                      /* Remember the new port */
++                      if (curr->count < SCAN_MAX_COUNT) {
++                              curr->ports[curr->count].number = dest_port;
++                              curr->ports[curr->count].proto = proto;
++                              curr->ports[curr->count].and_flags = tcp_flags;
++                              curr->ports[curr->count].or_flags = tcp_flags;
++                              curr->count++;
++                      }
++
++                      goto out_no_match;
++              }
++
++              /* We know this address, but the entry is outdated. Mark it unused, and
++               * remove from the hash table. We'll allocate a new entry instead since
++               * this one might get re-used too soon. */
++              curr->src_addr.s_addr = 0;
++              if (last)
++                      last->next = last->next->next;
++              else if (*head)
++                      *head = (*head)->next;
++              last = NULL;
++      }
++
++      /* We don't need an ACK from a new source address */
++      if (proto == IPPROTO_TCP && tcp_hdr->ack)
++              goto out_no_match;
++
++      /* Got too many source addresses with the same hash value? Then remove the
++       * oldest one from the hash table, so that they can't take too much of our
++       * CPU time even with carefully chosen spoofed IP addresses. */
++      if (count >= HASH_MAX && last) last->next = NULL;
++
++      /* We're going to re-use the oldest list entry, so remove it from the hash
++       * table first (if it is really already in use, and isn't removed from the
++       * hash table already because of the HASH_MAX check above). */
++
++      /* First, find it */
++      if (state.list[state.index].src_addr.s_addr)
++              head = &state.hash[hashfunc(state.list[state.index].src_addr)];
++      else
++              head = &last;
++      last = NULL;
++      if ((curr = *head))
++      do {
++              if (curr == &state.list[state.index]) break;
++              last = curr;
++      } while ((curr = curr->next));
++
++      /* Then, remove it */
++      if (curr) {
++              if (last)
++                      last->next = last->next->next;
++              else if (*head)
++                      *head = (*head)->next;
++      }
++
++      /* Get our list entry */
++      curr = &state.list[state.index++];
++      if (state.index >= LIST_SIZE) state.index = 0;
++
++      /* Link it into the hash table */
++      head = &state.hash[hash];
++      curr->next = *head;
++      *head = curr;
++
++      /* And fill in the fields */
++      curr->timestamp = now;
++      curr->src_addr = addr;
++      curr->dest_addr.s_addr = ip_hdr->daddr;
++      curr->src_port = src_port;
++      curr->count = 1;
++      curr->weight = (ntohs(dest_port) < 1024) ?
++              psdinfo->lo_ports_weight : psdinfo->hi_ports_weight;
++      curr->ports[0].number = dest_port;
++      curr->ports[0].proto = proto;
++      curr->ports[0].and_flags = tcp_flags;
++      curr->ports[0].or_flags = tcp_flags;
++      curr->tos = ip_hdr->tos;
++      curr->ttl = ip_hdr->ttl;
++
++out_no_match:
++      spin_unlock(&state.lock);
++      return 0;
++
++out_match:
++      spin_unlock(&state.lock);
++      return 1;
++}
++
++static int ipt_psd_checkentry(const char *tablename,
++                            const struct ipt_ip *e,
++                            void *matchinfo,
++                            unsigned int matchsize,
++                            unsigned int hook_mask)
++{
++/*    const struct ipt_psd_info *psdinfo = targinfo;*/
++
++      /* we accept TCP only */
++/*    if (e->ip.proto != IPPROTO_TCP) { */
++/*            DEBUGP("PSD: specified protocol may be TCP only\n"); */
++/*            return 0; */
++/*    } */
++
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_psd_info))) {
++              DEBUGP("PSD: matchsize %u != %u\n",
++                     matchsize,
++                     IPT_ALIGN(sizeof(struct ipt_psd_info)));
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ipt_match ipt_psd_reg = { 
++      {NULL, NULL},
++      "psd",
++      ipt_psd_match,
++      ipt_psd_checkentry,
++      NULL,
++      THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ipt_register_match(&ipt_psd_reg))
++              return -EINVAL;
++
++      memset(&state, 0, sizeof(state));
++
++      spin_lock_init(&(state.lock));
++
++      printk("netfilter PSD loaded - (c) astaro AG\n");
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&ipt_psd_reg);
++      printk("netfilter PSD unloaded - (c) astaro AG\n");
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_quota.c linux-2.4.20/net/ipv4/netfilter/ipt_quota.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_quota.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_quota.c        Wed Sep 24 09:17:03 2003
+@@ -0,0 +1,81 @@
++/* 
++ * netfilter module to enforce network quotas
++ *
++ * Sam Johnston <samj@samj.net>
++ */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/spinlock.h>
++#include <linux/interrupt.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_quota.h>
++
++MODULE_LICENSE("GPL");
++
++static spinlock_t quota_lock = SPIN_LOCK_UNLOCKED;
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset, const void *hdr, u_int16_t datalen, int *hotdrop)
++{
++
++        struct ipt_quota_info *q = (struct ipt_quota_info *) matchinfo;
++
++        spin_lock_bh(&quota_lock);
++
++        if (q->quota >= datalen) {
++                /* we can afford this one */
++                q->quota -= datalen;
++                spin_unlock_bh(&quota_lock);
++
++#ifdef DEBUG_IPT_QUOTA
++                printk("IPT Quota OK: %llu datlen %d \n", q->quota, datalen);
++#endif
++                return 1;
++        }
++
++        /* so we do not allow even small packets from now on */
++        q->quota = 0;
++
++#ifdef DEBUG_IPT_QUOTA
++        printk("IPT Quota Failed: %llu datlen %d \n", q->quota, datalen);
++#endif
++
++        spin_unlock_bh(&quota_lock);
++        return 0;
++}
++
++static int
++checkentry(const char *tablename,
++           const struct ipt_ip *ip,
++           void *matchinfo, unsigned int matchsize, unsigned int hook_mask)
++{
++        /* TODO: spinlocks? sanity checks? */
++        if (matchsize != IPT_ALIGN(sizeof (struct ipt_quota_info)))
++                return 0;
++
++        return 1;
++}
++
++static struct ipt_match quota_match
++    = { {NULL, NULL}, "quota", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init
++init(void)
++{
++        return ipt_register_match(&quota_match);
++}
++
++static void __exit
++fini(void)
++{
++        ipt_unregister_match(&quota_match);
++}
++
++module_init(init);
++module_exit(fini);
++
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_random.c linux-2.4.20/net/ipv4/netfilter/ipt_random.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_random.c   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_random.c       Wed Sep 24 09:17:05 2003
+@@ -0,0 +1,96 @@
++/*
++  This is a module which is used for a "random" match support.
++  This file is distributed under the terms of the GNU General Public
++  License (GPL). Copies of the GPL can be obtained from:
++     ftp://prep.ai.mit.edu/pub/gnu/GPL
++
++  2001-10-14 Fabrice MARIE <fabrice@netfilter.org> : initial implementation.
++*/
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <linux/random.h>
++#include <net/tcp.h>
++#include <linux/spinlock.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_random.h>
++
++MODULE_LICENSE("GPL");
++
++static int
++ipt_rand_match(const struct sk_buff *pskb,
++             const struct net_device *in,
++             const struct net_device *out,
++             const void *matchinfo,
++             int offset,
++             const void *hdr,
++             u_int16_t datalen,
++             int *hotdrop)
++{
++      /* Parameters from userspace */
++      const struct ipt_rand_info *info = matchinfo;
++      u_int8_t random_number;
++
++      /* get 1 random number from the kernel random number generation routine */
++      get_random_bytes((void *)(&random_number), 1);
++
++      /* Do we match ? */
++      if (random_number <= info->average)
++              return 1;
++      else
++              return 0;
++}
++
++static int
++ipt_rand_checkentry(const char *tablename,
++                 const struct ipt_ip *e,
++                 void *matchinfo,
++                 unsigned int matchsize,
++                 unsigned int hook_mask)
++{
++      /* Parameters from userspace */
++      const struct ipt_rand_info *info = matchinfo;
++
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_rand_info))) {
++              printk("ipt_random: matchsize %u != %u\n", matchsize,
++                     IPT_ALIGN(sizeof(struct ipt_rand_info)));
++              return 0;
++      }
++
++      /* must be  1 <= average % <= 99 */
++      /* 1  x 2.55 = 2   */
++      /* 99 x 2.55 = 252 */
++      if ((info->average < 2) || (info->average > 252)) {
++              printk("ipt_random:  invalid average %u\n", info->average);
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ipt_match ipt_rand_reg = { 
++      {NULL, NULL},
++      "random",
++      ipt_rand_match,
++      ipt_rand_checkentry,
++      NULL,
++      THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ipt_register_match(&ipt_rand_reg))
++              return -EINVAL;
++
++      printk("ipt_random match loaded\n");
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&ipt_rand_reg);
++      printk("ipt_random match unloaded\n");
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_realm.c linux-2.4.20/net/ipv4/netfilter/ipt_realm.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_realm.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_realm.c        Wed Sep 24 09:17:09 2003
+@@ -0,0 +1,68 @@
++/* Kernel module to match realm from routing. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netdevice.h>
++#include <net/route.h>
++
++#include <linux/netfilter_ipv4/ipt_realm.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++MODULE_AUTHOR("Sampsa Ranta <sampsa@netsonic.fi>");
++MODULE_LICENSE("GPL");
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      const struct ipt_realm_info *info = matchinfo;
++      struct dst_entry *dst = skb->dst;
++      u32 id;
++    
++      if(dst == NULL)
++              return 0;
++      id = dst->tclassid;
++
++      return (info->id == (id & info->mask)) ^ info->invert;
++}
++
++static int check(const char *tablename,
++                 const struct ipt_ip *ip,
++                 void *matchinfo,
++                 unsigned int matchsize,
++                 unsigned int hook_mask)
++{
++      if (hook_mask
++          & ~((1 << NF_IP_POST_ROUTING) | (1 << NF_IP_FORWARD) |
++              (1 << NF_IP_LOCAL_OUT)| (1 << NF_IP_LOCAL_IN))) {
++              printk("ipt_realm: only valid for POST_ROUTING, LOCAL_OUT, "
++                     "LOCAL_IN or FORWARD.\n");
++              return 0;
++      }
++
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_realm_info)))
++              return 0;
++
++      return 1;
++}
++
++static struct ipt_match realm_match
++= { { NULL, NULL }, "realm", &match, &check, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      return ipt_register_match(&realm_match);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&realm_match);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_recent.c linux-2.4.20/net/ipv4/netfilter/ipt_recent.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_recent.c   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_recent.c       Wed Sep 24 09:16:20 2003
+@@ -0,0 +1,1002 @@
++/* Kernel module to check if the source address has been seen recently. */
++/* Copyright 2002-2003, Stephen Frost */
++/* Author: Stephen Frost <sfrost@snowman.net> */
++/* Project Page: http://snowman.net/projects/ipt_recent/ */
++/* This software is distributed under the terms of the GPL, Version 2 */
++/* This copyright does not cover user programs that use kernel services
++ * by normal system calls. */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/proc_fs.h>
++#include <linux/spinlock.h>
++#include <linux/interrupt.h>
++#include <asm/uaccess.h>
++#include <linux/ctype.h>
++#include <linux/ip.h>
++#include <linux/vmalloc.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_recent.h>
++
++#undef DEBUG
++#define HASH_LOG 9
++
++/* Defaults, these can be overridden on the module command-line. */
++static int ip_list_tot = 100;
++static int ip_pkt_list_tot = 20;
++static int ip_list_hash_size = 0;
++static int ip_list_perms = 0644;
++#ifdef DEBUG
++static int debug = 1;
++#endif
++
++static char version[] =
++KERN_INFO RECENT_NAME " " RECENT_VER ": Stephen Frost <sfrost@snowman.net>.  http://snowman.net/projects/ipt_recent/\n";
++
++MODULE_AUTHOR("Stephen Frost <sfrost@snowman.net>");
++MODULE_DESCRIPTION("IP tables recently seen matching module " RECENT_VER);
++MODULE_LICENSE("GPL");
++MODULE_PARM(ip_list_tot,"i");
++MODULE_PARM(ip_pkt_list_tot,"i");
++MODULE_PARM(ip_list_hash_size,"i");
++MODULE_PARM(ip_list_perms,"i");
++#ifdef DEBUG
++MODULE_PARM(debug,"i");
++MODULE_PARM_DESC(debug,"debugging level, defaults to 1");
++#endif
++MODULE_PARM_DESC(ip_list_tot,"number of IPs to remember per list");
++MODULE_PARM_DESC(ip_pkt_list_tot,"number of packets per IP to remember");
++MODULE_PARM_DESC(ip_list_hash_size,"size of hash table used to look up IPs");
++MODULE_PARM_DESC(ip_list_perms,"permissions on /proc/net/ipt_recent/* files");
++
++/* Structure of our list of recently seen addresses. */
++struct recent_ip_list {
++      u_int32_t addr;
++      u_int8_t  ttl;
++      u_int32_t last_seen;
++      u_int32_t *last_pkts;
++      u_int32_t oldest_pkt;
++      u_int32_t hash_entry;
++      u_int32_t time_pos;
++};
++
++struct time_info_list {
++      u_int32_t position;
++      u_int32_t time;
++};
++
++/* Structure of our linked list of tables of recent lists. */
++struct recent_ip_tables {
++      char name[IPT_RECENT_NAME_LEN];
++      int count;
++      int time_pos;
++      struct recent_ip_list *table;
++      struct recent_ip_tables *next;
++      spinlock_t list_lock;
++      int *hash_table;
++      struct time_info_list *time_info;
++#ifdef CONFIG_PROC_FS
++      struct proc_dir_entry *status_proc;
++#endif /* CONFIG_PROC_FS */
++};
++
++/* Our current list of addresses we have recently seen.
++ * Only added to on a --set, and only updated on --set || --update 
++ */
++static struct recent_ip_tables *r_tables = NULL;
++
++/* We protect r_list with this spinlock so two processors are not modifying
++ * the list at the same time. 
++ */
++static spinlock_t recent_lock = SPIN_LOCK_UNLOCKED;
++
++#ifdef CONFIG_PROC_FS
++/* Our /proc/net/ipt_recent entry */
++static struct proc_dir_entry *proc_net_ipt_recent = NULL;
++#endif
++
++/* Function declaration for later. */
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop);
++
++/* Function to hash a given address into the hash table of table_size size */
++int hash_func(unsigned int addr, int table_size)
++{
++      int result = 0;
++      unsigned int value = addr;
++      do { result ^= value; } while((value >>= HASH_LOG));
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": %d = hash_func(%u,%d)\n",
++                       result & (table_size - 1),
++                       addr,
++                       table_size);
++#endif
++
++      return(result & (table_size - 1));
++}
++
++#ifdef CONFIG_PROC_FS
++/* This is the function which produces the output for our /proc output
++ * interface which lists each IP address, the last seen time and the 
++ * other recent times the address was seen.
++ */
++
++static int ip_recent_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
++{
++      int len = 0, count, last_len = 0, pkt_count;
++      off_t pos = 0;
++      off_t begin = 0;
++      struct recent_ip_tables *curr_table;
++
++      curr_table = (struct recent_ip_tables*) data;
++
++      spin_lock_bh(&curr_table->list_lock);
++      for(count = 0; count < ip_list_tot; count++) {
++              if(!curr_table->table[count].addr) continue;
++              last_len = len;
++              len += sprintf(buffer+len,"src=%u.%u.%u.%u ",NIPQUAD(curr_table->table[count].addr));
++              len += sprintf(buffer+len,"ttl: %u ",curr_table->table[count].ttl);
++              len += sprintf(buffer+len,"last_seen: %u ",curr_table->table[count].last_seen);
++              len += sprintf(buffer+len,"oldest_pkt: %u ",curr_table->table[count].oldest_pkt);
++              len += sprintf(buffer+len,"last_pkts: %u",curr_table->table[count].last_pkts[0]);
++              for(pkt_count = 1; pkt_count < ip_pkt_list_tot; pkt_count++) {
++                      if(!curr_table->table[count].last_pkts[pkt_count]) break;
++                      len += sprintf(buffer+len,", %u",curr_table->table[count].last_pkts[pkt_count]);
++              }
++              len += sprintf(buffer+len,"\n");
++              pos = begin + len;
++              if(pos < offset) { len = 0; begin = pos; }
++              if(pos > offset + length) { len = last_len; break; }
++      }
++
++      *start = buffer + (offset - begin);
++      len -= (offset - begin);
++      if(len > length) len = length;
++
++      spin_unlock_bh(&curr_table->list_lock);
++      return len;
++}
++
++/* ip_recent_ctrl provides an interface for users to modify the table
++ * directly.  This allows adding entries, removing entries, and
++ * flushing the entire table.
++ * This is done by opening up the appropriate table for writing and
++ * sending one of:
++ * xx.xx.xx.xx   -- Add entry to table with current time
++ * +xx.xx.xx.xx  -- Add entry to table with current time
++ * -xx.xx.xx.xx  -- Remove entry from table
++ * clear         -- Flush table, remove all entries
++ */
++
++static int ip_recent_ctrl(struct file *file, const char *input, unsigned long size, void *data)
++{
++      static const u_int32_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
++      u_int32_t val;
++      int base, used = 0;
++      char c, *cp;
++      union iaddr {
++              uint8_t bytes[4];
++              uint32_t word;
++      } res;
++      uint8_t *pp = res.bytes;
++      int digit;
++
++      char buffer[20];
++      int len, check_set = 0, count;
++      u_int32_t addr = 0;
++      struct sk_buff *skb;
++      struct ipt_recent_info *info;
++      struct recent_ip_tables *curr_table;
++
++      curr_table = (struct recent_ip_tables*) data;
++
++      if(size > 20) len = 20; else len = size;
++
++      if(copy_from_user(buffer,input,len)) return -EFAULT;
++
++      if(len < 20) buffer[len] = '\0';
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl len: %d, input: `%.20s'\n",len,buffer);
++#endif
++
++      cp = buffer;
++      while(isspace(*cp)) { cp++; used++; if(used >= len-5) return used; }
++
++      /* Check if we are asked to flush the entire table */
++      if(!memcmp(cp,"clear",5)) {
++              used += 5;
++              spin_lock_bh(&curr_table->list_lock);
++              curr_table->time_pos = 0;
++              for(count = 0; count < ip_list_hash_size; count++) {
++                      curr_table->hash_table[count] = -1;
++              }
++              for(count = 0; count < ip_list_tot; count++) {
++                      curr_table->table[count].last_seen = 0;
++                      curr_table->table[count].addr = 0;
++                      curr_table->table[count].ttl = 0;
++                      memset(curr_table->table[count].last_pkts,0,ip_pkt_list_tot*sizeof(u_int32_t));
++                      curr_table->table[count].oldest_pkt = 0;
++                      curr_table->table[count].time_pos = 0;
++                      curr_table->time_info[count].position = count;
++                      curr_table->time_info[count].time = 0;
++              }
++              spin_unlock_bh(&curr_table->list_lock);
++              return used;
++      }
++
++        check_set = IPT_RECENT_SET;
++      switch(*cp) {
++              case '+': check_set = IPT_RECENT_SET; cp++; used++; break;
++              case '-': check_set = IPT_RECENT_REMOVE; cp++; used++; break;
++              default: if(!isdigit(*cp)) return (used+1); break;
++      }
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl cp: `%c', check_set: %d\n",*cp,check_set);
++#endif
++      /* Get addr (effectively inet_aton()) */
++      /* Shamelessly stolen from libc, a function in the kernel for doing
++       * this would, of course, be greatly preferred, but our options appear
++       * to be rather limited, so we will just do it ourselves here.
++       */
++      res.word = 0;
++
++      c = *cp;
++      for(;;) {
++              if(!isdigit(c)) return used;
++              val = 0; base = 10; digit = 0;
++              if(c == '0') {
++                      c = *++cp;
++                      if(c == 'x' || c == 'X') base = 16, c = *++cp;
++                      else { base = 8; digit = 1; }
++              }
++              for(;;) {
++                      if(isascii(c) && isdigit(c)) {
++                              if(base == 8 && (c == '8' || c == '0')) return used;
++                              val = (val * base) + (c - '0');
++                              c = *++cp;
++                              digit = 1;
++                      } else if(base == 16 && isascii(c) && isxdigit(c)) {
++                              val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A'));
++                              c = *++cp;
++                              digit = 1;
++                      } else break;
++              }
++              if(c == '.') {
++                      if(pp > res.bytes + 2 || val > 0xff) return used;
++                      *pp++ = val;
++                      c = *++cp;
++              } else break;
++      }
++      used = cp - buffer;
++      if(c != '\0' && (!isascii(c) || !isspace(c))) return used;
++      if(c == '\n') used++;
++      if(!digit) return used;
++
++      if(val > max[pp - res.bytes]) return used;
++      addr = res.word | htonl(val);
++
++      if(!addr && check_set == IPT_RECENT_SET) return used;
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl c: %c, addr: %u used: %d\n",c,addr,used);
++#endif
++
++      /* Set up and just call match */
++      info = kmalloc(sizeof(struct ipt_recent_info),GFP_KERNEL);
++      if(!info) { return -ENOMEM; }
++      info->seconds = 0;
++      info->hit_count = 0;
++      info->check_set = check_set;
++      info->invert = 0;
++      info->side = IPT_RECENT_SOURCE;
++      strncpy(info->name,curr_table->name,IPT_RECENT_NAME_LEN);
++      info->name[IPT_RECENT_NAME_LEN-1] = '\0';
++
++      skb = kmalloc(sizeof(struct sk_buff),GFP_KERNEL);
++      if (!skb) {
++              used = -ENOMEM;
++              goto out_free_info;
++      }
++      skb->nh.iph = kmalloc(sizeof(struct iphdr),GFP_KERNEL);
++      if (!skb->nh.iph) {
++              used = -ENOMEM;
++              goto out_free_skb;
++      }
++
++      skb->nh.iph->saddr = addr;
++      skb->nh.iph->daddr = 0;
++      /* Clear ttl since we have no way of knowing it */
++      skb->nh.iph->ttl = 0;
++      match(skb,NULL,NULL,info,0,NULL,sizeof(struct ipt_recent_info),NULL);
++
++      kfree(skb->nh.iph);
++out_free_skb:
++      kfree(skb);
++out_free_info:
++      kfree(info);
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": Leaving ip_recent_ctrl addr: %u used: %d\n",addr,used);
++#endif
++      return used;
++}
++
++#endif /* CONFIG_PROC_FS */
++
++/* 'match' is our primary function, called by the kernel whenever a rule is
++ * hit with our module as an option to it.
++ * What this function does depends on what was specifically asked of it by
++ * the user:
++ * --set -- Add or update last seen time of the source address of the packet
++ *   -- matchinfo->check_set == IPT_RECENT_SET
++ * --rcheck -- Just check if the source address is in the list
++ *   -- matchinfo->check_set == IPT_RECENT_CHECK
++ * --update -- If the source address is in the list, update last_seen
++ *   -- matchinfo->check_set == IPT_RECENT_UPDATE
++ * --remove -- If the source address is in the list, remove it
++ *   -- matchinfo->check_set == IPT_RECENT_REMOVE
++ * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds
++ *   -- matchinfo->seconds
++ * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times
++ *   -- matchinfo->hit_count
++ * --seconds and --hitcount can be combined
++ */
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      int pkt_count, hits_found, ans;
++      unsigned long now;
++      const struct ipt_recent_info *info = matchinfo;
++      u_int32_t addr = 0, time_temp;
++      u_int8_t ttl = skb->nh.iph->ttl;
++      int *hash_table;
++      int orig_hash_result, hash_result, temp, location = 0, time_loc, end_collision_chain = -1;
++      struct time_info_list *time_info;
++      struct recent_ip_tables *curr_table;
++      struct recent_ip_tables *last_table;
++      struct recent_ip_list *r_list;
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": match() called\n");
++#endif
++
++      /* Default is false ^ info->invert */
++      ans = info->invert;
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": match(): name = '%s'\n",info->name);
++#endif
++
++      /* if out != NULL then routing has been done and TTL changed.
++       * We change it back here internally for match what came in before routing. */
++      if(out) ttl++;
++
++      /* Find the right table */
++      spin_lock_bh(&recent_lock);
++      curr_table = r_tables;
++      while( (last_table = curr_table) && strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (curr_table = curr_table->next) );
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": match(): table found('%s')\n",info->name);
++#endif
++
++      spin_unlock_bh(&recent_lock);
++
++      /* Table with this name not found, match impossible */
++      if(!curr_table) { return ans; }
++
++      /* Make sure no one is changing the list while we work with it */
++      spin_lock_bh(&curr_table->list_lock);
++
++      r_list = curr_table->table;
++      if(info->side == IPT_RECENT_DEST) addr = skb->nh.iph->daddr; else addr = skb->nh.iph->saddr;
++
++      if(!addr) { 
++#ifdef DEBUG
++              if(debug) printk(KERN_INFO RECENT_NAME ": match() address (%u) invalid, leaving.\n",addr);
++#endif
++              spin_unlock_bh(&curr_table->list_lock);
++              return ans;
++      }
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": match(): checking table, addr: %u, ttl: %u, orig_ttl: %u\n",addr,ttl,skb->nh.iph->ttl);
++#endif
++
++      /* Get jiffies now in case they changed while we were waiting for a lock */
++      now = jiffies;
++      hash_table = curr_table->hash_table;
++      time_info = curr_table->time_info;
++
++      orig_hash_result = hash_result = hash_func(addr,ip_list_hash_size);
++      /* Hash entry at this result used */
++      /* Check for TTL match if requested.  If TTL is zero then a match would never
++       * happen, so match regardless of existing TTL in that case.  Zero means the
++       * entry was added via the /proc interface anyway, so we will just use the
++       * first TTL we get for that IP address. */
++      if(info->check_set & IPT_RECENT_TTL) {
++              while(hash_table[hash_result] != -1 && !(r_list[hash_table[hash_result]].addr == addr &&
++                      (!r_list[hash_table[hash_result]].ttl || r_list[hash_table[hash_result]].ttl == ttl))) {
++                      /* Collision in hash table */
++                      hash_result = (hash_result + 1) % ip_list_hash_size;
++              }
++      } else {
++              while(hash_table[hash_result] != -1 && r_list[hash_table[hash_result]].addr != addr) {
++                      /* Collision in hash table */
++                      hash_result = (hash_result + 1) % ip_list_hash_size;
++              }
++      }
++
++      if(hash_table[hash_result] == -1 && !(info->check_set & IPT_RECENT_SET)) {
++              /* IP not in list and not asked to SET */
++              spin_unlock_bh(&curr_table->list_lock);
++              return ans;
++      }
++
++      /* Check if we need to handle the collision, do not need to on REMOVE */
++      if(orig_hash_result != hash_result && !(info->check_set & IPT_RECENT_REMOVE)) {
++#ifdef DEBUG
++              if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision in hash table. (or: %d,hr: %d,oa: %u,ha: %u)\n",
++                               orig_hash_result,
++                               hash_result,
++                               r_list[hash_table[orig_hash_result]].addr,
++                               addr);
++#endif
++
++              /* We had a collision.
++               * orig_hash_result is where we started, hash_result is where we ended up.
++               * So, swap them because we are likely to see the same guy again sooner */
++#ifdef DEBUG
++              if(debug) {
++                printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[orig_hash_result] = %d\n",hash_table[orig_hash_result]);
++                printk(KERN_INFO RECENT_NAME ": match(): Collision; r_list[hash_table[orig_hash_result]].hash_entry = %d\n",
++                              r_list[hash_table[orig_hash_result]].hash_entry);
++              }
++#endif
++
++              r_list[hash_table[orig_hash_result]].hash_entry = hash_result;
++
++
++              temp = hash_table[orig_hash_result];
++#ifdef DEBUG
++              if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[hash_result] = %d\n",hash_table[hash_result]);
++#endif
++              hash_table[orig_hash_result] = hash_table[hash_result];
++              hash_table[hash_result] = temp;
++              temp = hash_result;
++              hash_result = orig_hash_result;
++              orig_hash_result = temp;
++              time_info[r_list[hash_table[orig_hash_result]].time_pos].position = hash_table[orig_hash_result];
++              if(hash_table[hash_result] != -1) {
++                      r_list[hash_table[hash_result]].hash_entry = hash_result;
++                      time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
++              }
++
++#ifdef DEBUG
++              if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision handled.\n");
++#endif
++      }
++
++      if(hash_table[hash_result] == -1) {
++#ifdef DEBUG
++              if(debug) printk(KERN_INFO RECENT_NAME ": match(): New table entry. (hr: %d,ha: %u)\n",
++                               hash_result, addr);
++#endif
++
++              /* New item found and IPT_RECENT_SET, so we need to add it */
++              location = time_info[curr_table->time_pos].position;
++              hash_table[r_list[location].hash_entry] = -1;
++              hash_table[hash_result] = location;
++              memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(u_int32_t));
++              r_list[location].time_pos = curr_table->time_pos;
++              r_list[location].addr = addr;
++              r_list[location].ttl = ttl;
++              r_list[location].last_seen = now;
++              r_list[location].oldest_pkt = 1;
++              r_list[location].last_pkts[0] = now;
++              r_list[location].hash_entry = hash_result;
++              time_info[curr_table->time_pos].time = r_list[location].last_seen;
++              curr_table->time_pos = (curr_table->time_pos + 1) % ip_list_tot;
++
++              ans = !info->invert;
++      } else {
++#ifdef DEBUG
++              if(debug) printk(KERN_INFO RECENT_NAME ": match(): Existing table entry. (hr: %d,ha: %u)\n",
++                               hash_result,
++                               addr);
++#endif
++
++              /* Existing item found */
++              location = hash_table[hash_result];
++              /* We have a match on address, now to make sure it meets all requirements for a
++               * full match. */
++              if(info->check_set & IPT_RECENT_CHECK || info->check_set & IPT_RECENT_UPDATE) {
++                      if(!info->seconds && !info->hit_count) ans = !info->invert; else ans = info->invert;
++                      if(info->seconds && !info->hit_count) {
++                              if(time_before_eq(now,r_list[location].last_seen+info->seconds*HZ)) ans = !info->invert; else ans = info->invert;
++                      }
++                      if(info->seconds && info->hit_count) {
++                              for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
++                                      if(time_before_eq(now,r_list[location].last_pkts[pkt_count]+info->seconds*HZ)) hits_found++;
++                              }
++                              if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
++                      }
++                      if(info->hit_count && !info->seconds) {
++                              for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) {
++                                      if(r_list[location].last_pkts[pkt_count] == 0) break;
++                                      hits_found++;
++                              }
++                              if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert;
++                      }
++              }
++#ifdef DEBUG
++              if(debug) {
++                      if(ans)
++                              printk(KERN_INFO RECENT_NAME ": match(): match addr: %u\n",addr);
++                      else
++                              printk(KERN_INFO RECENT_NAME ": match(): no match addr: %u\n",addr);
++              }
++#endif
++
++              /* If and only if we have been asked to SET, or to UPDATE (on match) do we add the
++               * current timestamp to the last_seen. */
++              if((info->check_set & IPT_RECENT_SET && (ans = !info->invert)) || (info->check_set & IPT_RECENT_UPDATE && ans)) {
++#ifdef DEBUG
++                      if(debug) printk(KERN_INFO RECENT_NAME ": match(): SET or UPDATE; updating time info.\n");
++#endif
++                      /* Have to update our time info */
++                      time_loc = r_list[location].time_pos;
++                      time_info[time_loc].time = now;
++                      time_info[time_loc].position = location;
++                      while((time_info[(time_loc+1) % ip_list_tot].time < time_info[time_loc].time) && ((time_loc+1) % ip_list_tot) != curr_table->time_pos) {
++                              time_temp = time_info[time_loc].time;
++                              time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
++                              time_info[(time_loc+1)%ip_list_tot].time = time_temp;
++                              time_temp = time_info[time_loc].position;
++                              time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
++                              time_info[(time_loc+1)%ip_list_tot].position = time_temp;
++                              r_list[time_info[time_loc].position].time_pos = time_loc;
++                              r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
++                              time_loc = (time_loc+1) % ip_list_tot;
++                      }
++                      r_list[location].time_pos = time_loc;
++                      r_list[location].ttl = ttl;
++                      r_list[location].last_pkts[r_list[location].oldest_pkt] = now;
++                      r_list[location].oldest_pkt = ++r_list[location].oldest_pkt % ip_pkt_list_tot;
++                      r_list[location].last_seen = now;
++              }
++              /* If we have been asked to remove the entry from the list, just set it to 0 */
++              if(info->check_set & IPT_RECENT_REMOVE) {
++#ifdef DEBUG
++                      if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; clearing entry (or: %d, hr: %d).\n",orig_hash_result,hash_result);
++#endif
++                      /* Check if this is part of a collision chain */
++                      while(hash_table[(orig_hash_result+1) % ip_list_hash_size] != -1) {
++                              orig_hash_result++;
++                              if(hash_func(r_list[hash_table[orig_hash_result]].addr,ip_list_hash_size) == hash_result) {
++                                      /* Found collision chain, how deep does this rabbit hole go? */
++#ifdef DEBUG
++                                      if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; found collision chain.\n");
++#endif
++                                      end_collision_chain = orig_hash_result;
++                              }
++                      }
++                      if(end_collision_chain != -1) {
++#ifdef DEBUG
++                              if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; part of collision chain, moving to end.\n");
++#endif
++                              /* Part of a collision chain, swap it with the end of the chain
++                               * before removing. */
++                              r_list[hash_table[end_collision_chain]].hash_entry = hash_result;
++                              temp = hash_table[end_collision_chain];
++                              hash_table[end_collision_chain] = hash_table[hash_result];
++                              hash_table[hash_result] = temp;
++                              time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
++                              hash_result = end_collision_chain;
++                              r_list[hash_table[hash_result]].hash_entry = hash_result;
++                              time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result];
++                      }
++                      location = hash_table[hash_result];
++                      hash_table[r_list[location].hash_entry] = -1;
++                      time_loc = r_list[location].time_pos;
++                      time_info[time_loc].time = 0;
++                      time_info[time_loc].position = location;
++                      while((time_info[(time_loc+1) % ip_list_tot].time < time_info[time_loc].time) && ((time_loc+1) % ip_list_tot) != curr_table->time_pos) {
++                              time_temp = time_info[time_loc].time;
++                              time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time;
++                              time_info[(time_loc+1)%ip_list_tot].time = time_temp;
++                              time_temp = time_info[time_loc].position;
++                              time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position;
++                              time_info[(time_loc+1)%ip_list_tot].position = time_temp;
++                              r_list[time_info[time_loc].position].time_pos = time_loc;
++                              r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot;
++                              time_loc = (time_loc+1) % ip_list_tot;
++                      }
++                      r_list[location].time_pos = time_loc;
++                      r_list[location].last_seen = 0;
++                      r_list[location].addr = 0;
++                      r_list[location].ttl = 0;
++                      memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(u_int32_t));
++                      r_list[location].oldest_pkt = 0;
++                      ans = !info->invert;
++              }
++              spin_unlock_bh(&curr_table->list_lock);
++              return ans;
++      }
++
++      spin_unlock_bh(&curr_table->list_lock);
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": match() left.\n");
++#endif
++      return ans;
++}
++
++/* This function is to verify that the rule given during the userspace iptables
++ * command is correct.
++ * If the command is valid then we check if the table name referred to by the
++ * rule exists, if not it is created.
++ */
++static int
++checkentry(const char *tablename,
++           const struct ipt_ip *ip,
++           void *matchinfo,
++           unsigned int matchsize,
++           unsigned int hook_mask)
++{
++      int flag = 0, c;
++      u_int32_t *hold;
++      const struct ipt_recent_info *info = matchinfo;
++      struct recent_ip_tables *curr_table, *find_table, *last_table;
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() entered.\n");
++#endif
++
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return 0;
++
++      /* seconds and hit_count only valid for CHECK/UPDATE */
++      if(info->check_set & IPT_RECENT_SET) { flag++; if(info->seconds || info->hit_count) return 0; }
++      if(info->check_set & IPT_RECENT_REMOVE) { flag++; if(info->seconds || info->hit_count) return 0; }
++      if(info->check_set & IPT_RECENT_CHECK) flag++;
++      if(info->check_set & IPT_RECENT_UPDATE) flag++;
++
++      /* One and only one of these should ever be set */
++      if(flag != 1) return 0;
++
++      /* Name must be set to something */
++      if(!info->name || !info->name[0]) return 0;
++
++      /* Things look good, create a list for this if it does not exist */
++      /* Lock the linked list while we play with it */
++      spin_lock_bh(&recent_lock);
++
++      /* Look for an entry with this name already created */
++      /* Finds the end of the list and the entry before the end if current name does not exist */
++      find_table = r_tables;
++      while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
++
++      /* If a table already exists just increment the count on that table and return */
++      if(find_table) { 
++#ifdef DEBUG
++              if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), incrementing count.\n",info->name);
++#endif
++              find_table->count++;
++              spin_unlock_bh(&recent_lock);
++              return 1;
++      }
++
++      spin_unlock_bh(&recent_lock);
++
++      /* Table with this name not found */
++      /* Allocate memory for new linked list item */
++
++#ifdef DEBUG
++      if(debug) {
++              printk(KERN_INFO RECENT_NAME ": checkentry: no table found (%s)\n",info->name);
++              printk(KERN_INFO RECENT_NAME ": checkentry: Allocationg %d for link-list entry.\n",sizeof(struct recent_ip_tables));
++      }
++#endif
++
++      curr_table = vmalloc(sizeof(struct recent_ip_tables));
++      if(curr_table == NULL) return -ENOMEM;
++
++      curr_table->list_lock = SPIN_LOCK_UNLOCKED;
++      curr_table->next = NULL;
++      curr_table->count = 1;
++      curr_table->time_pos = 0;
++      strncpy(curr_table->name,info->name,IPT_RECENT_NAME_LEN);
++      curr_table->name[IPT_RECENT_NAME_LEN-1] = '\0';
++
++      /* Allocate memory for this table and the list of packets in each entry. */
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for table (%s).\n",
++                      sizeof(struct recent_ip_list)*ip_list_tot,
++                      info->name);
++#endif
++
++      curr_table->table = vmalloc(sizeof(struct recent_ip_list)*ip_list_tot);
++      if(curr_table->table == NULL) { vfree(curr_table); return -ENOMEM; }
++      memset(curr_table->table,0,sizeof(struct recent_ip_list)*ip_list_tot);
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for pkt_list.\n",
++                      sizeof(u_int32_t)*ip_pkt_list_tot*ip_list_tot);
++#endif
++
++      hold = vmalloc(sizeof(u_int32_t)*ip_pkt_list_tot*ip_list_tot);
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: After pkt_list allocation.\n");
++#endif
++      if(hold == NULL) { 
++              printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for pkt_list.\n");
++              vfree(curr_table->table); 
++              vfree(curr_table);
++              return -ENOMEM;
++      }
++      for(c = 0; c < ip_list_tot; c++) {
++              curr_table->table[c].last_pkts = hold + c*ip_pkt_list_tot;
++      }
++
++      /* Allocate memory for the hash table */
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for hash_table.\n",
++                      sizeof(int)*ip_list_hash_size);
++#endif
++
++      curr_table->hash_table = vmalloc(sizeof(int)*ip_list_hash_size);
++      if(!curr_table->hash_table) {
++              printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for hash_table.\n");
++              vfree(hold);
++              vfree(curr_table->table); 
++              vfree(curr_table);
++              return -ENOMEM;
++      }
++
++      for(c = 0; c < ip_list_hash_size; c++) {
++              curr_table->hash_table[c] = -1;
++      }
++
++      /* Allocate memory for the time info */
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for time_info.\n",
++                      sizeof(struct time_info_list)*ip_list_tot);
++#endif
++
++      curr_table->time_info = vmalloc(sizeof(struct time_info_list)*ip_list_tot);
++      if(!curr_table->time_info) {
++              printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for time_info.\n");
++              vfree(curr_table->hash_table);
++              vfree(hold);
++              vfree(curr_table->table); 
++              vfree(curr_table);
++              return -ENOMEM;
++      }
++      for(c = 0; c < ip_list_tot; c++) {
++              curr_table->time_info[c].position = c;
++              curr_table->time_info[c].time = 0;
++      }
++
++      /* Put the new table in place */
++      spin_lock_bh(&recent_lock);
++      find_table = r_tables;
++      while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) );
++
++      /* If a table already exists just increment the count on that table and return */
++      if(find_table) { 
++              find_table->count++;    
++              spin_unlock_bh(&recent_lock);
++#ifdef DEBUG
++              if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), created by other process.\n",info->name);
++#endif
++              vfree(curr_table->time_info);
++              vfree(curr_table->hash_table);
++              vfree(hold);
++              vfree(curr_table->table);
++              vfree(curr_table);
++              return 1;
++      }
++      if(!last_table) r_tables = curr_table; else last_table->next = curr_table;
++
++      spin_unlock_bh(&recent_lock);
++
++#ifdef CONFIG_PROC_FS
++      /* Create our proc 'status' entry. */
++      curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent);
++      if (!curr_table->status_proc) {
++              printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n");
++              /* Destroy the created table */
++              spin_lock_bh(&recent_lock);
++              last_table = NULL;
++              curr_table = r_tables;
++              if(!curr_table) {
++#ifdef DEBUG
++                      if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, no tables.\n");
++#endif
++                      spin_unlock_bh(&recent_lock);
++                      return -ENOMEM;
++              }
++              while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
++              if(!curr_table) {
++#ifdef DEBUG
++                      if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, table already destroyed.\n");
++#endif
++                      spin_unlock_bh(&recent_lock);
++                      return -ENOMEM;
++              }
++              if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
++              spin_unlock_bh(&recent_lock);
++              vfree(curr_table->time_info);
++              vfree(curr_table->hash_table);
++              vfree(hold);
++              vfree(curr_table->table);
++              vfree(curr_table);
++              return -ENOMEM;
++      }
++      
++      curr_table->status_proc->owner = THIS_MODULE;
++      curr_table->status_proc->data = curr_table;
++      wmb();
++      curr_table->status_proc->read_proc = ip_recent_get_info;
++      curr_table->status_proc->write_proc = ip_recent_ctrl;
++#endif /* CONFIG_PROC_FS */
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() left.\n");
++#endif
++
++      return 1;
++}
++
++/* This function is called in the event that a rule matching this module is
++ * removed.
++ * When this happens we need to check if there are no other rules matching
++ * the table given.  If that is the case then we remove the table and clean
++ * up its memory.
++ */
++static void
++destroy(void *matchinfo, unsigned int matchsize)
++{
++      const struct ipt_recent_info *info = matchinfo;
++      struct recent_ip_tables *curr_table, *last_table;
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": destroy() entered.\n");
++#endif
++
++      if(matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return;
++
++      /* Lock the linked list while we play with it */
++      spin_lock_bh(&recent_lock);
++
++      /* Look for an entry with this name already created */
++      /* Finds the end of the list and the entry before the end if current name does not exist */
++      last_table = NULL;
++      curr_table = r_tables;
++      if(!curr_table) { 
++#ifdef DEBUG
++              if(debug) printk(KERN_INFO RECENT_NAME ": destroy() No tables found, leaving.\n");
++#endif
++              spin_unlock_bh(&recent_lock);
++              return;
++      }
++      while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) );
++
++      /* If a table does not exist then do nothing and return */
++      if(!curr_table) { 
++#ifdef DEBUG
++              if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table not found, leaving.\n");
++#endif
++              spin_unlock_bh(&recent_lock);
++              return;
++      }
++
++      curr_table->count--;
++
++      /* If count is still non-zero then there are still rules referenceing it so we do nothing */
++      if(curr_table->count) { 
++#ifdef DEBUG
++              if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, non-zero count, leaving.\n");
++#endif
++              spin_unlock_bh(&recent_lock);
++              return;
++      }
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, zero count, removing.\n");
++#endif
++
++      /* Count must be zero so we remove this table from the list */
++      if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next;
++
++      spin_unlock_bh(&recent_lock);
++
++      /* lock to make sure any late-runners still using this after we removed it from
++       * the list finish up then remove everything */
++      spin_lock_bh(&curr_table->list_lock);
++      spin_unlock_bh(&curr_table->list_lock);
++
++#ifdef CONFIG_PROC_FS
++      if(curr_table->status_proc) remove_proc_entry(curr_table->name,proc_net_ipt_recent);
++#endif /* CONFIG_PROC_FS */
++      vfree(curr_table->table[0].last_pkts);
++      vfree(curr_table->table);
++      vfree(curr_table->hash_table);
++      vfree(curr_table->time_info);
++      vfree(curr_table);
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": destroy() left.\n");
++#endif
++
++      return;
++}
++
++/* This is the structure we pass to ipt_register to register our
++ * module with iptables.
++ */
++static struct ipt_match recent_match = { 
++  .name = "recent", 
++  .match = &match, 
++  .checkentry = &checkentry, 
++  .destroy = &destroy, 
++  .me = THIS_MODULE
++};
++
++/* Kernel module initialization. */
++static int __init init(void)
++{
++      int count;
++
++      printk(version);
++#ifdef CONFIG_PROC_FS
++      proc_net_ipt_recent = proc_mkdir("ipt_recent",proc_net);
++      if(!proc_net_ipt_recent) return -ENOMEM;
++#endif
++
++      if(ip_list_hash_size && ip_list_hash_size <= ip_list_tot) {
++        printk(KERN_WARNING RECENT_NAME ": ip_list_hash_size too small, resetting to default.\n");
++        ip_list_hash_size = 0;
++      }
++
++      if(!ip_list_hash_size) {
++              ip_list_hash_size = ip_list_tot*3;
++              count = 2*2;
++              while(ip_list_hash_size > count) count = count*2;
++              ip_list_hash_size = count;
++      }
++
++#ifdef DEBUG
++      if(debug) printk(KERN_INFO RECENT_NAME ": ip_list_hash_size: %d\n",ip_list_hash_size);
++#endif
++
++      return ipt_register_match(&recent_match);
++}
++
++/* Kernel module destruction. */
++static void __exit fini(void)
++{
++      ipt_unregister_match(&recent_match);
++
++      remove_proc_entry("ipt_recent",proc_net);
++}
++
++/* Register our module with the kernel. */
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_rpc.c linux-2.4.20/net/ipv4/netfilter/ipt_rpc.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_rpc.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_rpc.c  Wed Sep 24 09:18:01 2003
+@@ -0,0 +1,428 @@
++/* RPC extension for IP connection matching, Version 2.2
++ * (C) 2000 by Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>
++ *    - original rpc tracking module
++ *    - "recent" connection handling for kernel 2.3+ netfilter
++ *
++ * (C) 2001 by Rusty Russell <rusty@rustcorp.com.au>
++ *    - upgraded conntrack modules to oldnat api - kernel 2.4.0+
++ *
++ * (C) 2002,2003 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
++ *    - upgraded conntrack modules to newnat api - kernel 2.4.20+
++ *    - extended matching to support filtering on procedures
++ *
++ * ipt_rpc.c,v 2.2 2003/01/12 18:30:00
++ *
++ *    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 ipt_rpc.o ports=port1,port2,...port<MAX_PORTS>
++ *
++ *    Please give the ports of all RPC servers you wish to connect to.
++ *    If you don't specify ports, the default will be port 111.
++ **
++ *    Note to all:
++ *
++ *    RPCs should not be exposed to the internet - ask the Pentagon;
++ *
++ *      "The unidentified crackers pleaded guilty in July to charges
++ *       of juvenile delinquency stemming from a string of Pentagon
++ *       network intrusions in February.
++ *
++ *       The youths, going by the names TooShort and Makaveli, used
++ *       a common server security hole to break in, according to
++ *       Dane Jasper, owner of the California Internet service
++ *       provider, Sonic. They used the hole, known as the 'statd'
++ *       exploit, to attempt more than 800 break-ins, Jasper said."
++ *
++ *    From: Wired News; "Pentagon Kids Kicked Off Grid" - Nov 6, 1998
++ *    URL:  http://www.wired.com/news/politics/0,1283,16098,00.html
++ **
++ */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/list.h>
++#include <linux/udp.h>
++#include <linux/tcp.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_conntrack_rpc.h>
++#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/netfilter_ipv4/ipt_rpc.h>
++
++#define MAX_PORTS 8
++static int ports[MAX_PORTS];
++static int ports_n_c = 0;
++
++#ifdef MODULE_PARM
++MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
++MODULE_PARM_DESC(ports, "port numbers (TCP/UDP) of RPC portmapper servers");
++#endif
++
++MODULE_AUTHOR("Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>");
++MODULE_DESCRIPTION("RPC connection matching module");
++MODULE_LICENSE("GPL");
++
++#if 0
++#define DEBUGP(format, args...) printk(KERN_DEBUG "ipt_rpc: " \
++                                      format, ## args)
++#else
++#define DEBUGP(format, args...)
++#endif
++
++EXPORT_NO_SYMBOLS;
++
++/* vars from ip_conntrack_rpc_tcp */
++extern struct list_head request_p_list_tcp;
++extern struct module *ip_conntrack_rpc_tcp;
++
++/* vars from ip_conntrack_rpc_udp */
++extern struct list_head request_p_list_udp;
++extern struct module *ip_conntrack_rpc_udp;
++
++DECLARE_RWLOCK_EXTERN(ipct_rpc_tcp_lock);
++DECLARE_RWLOCK_EXTERN(ipct_rpc_udp_lock);
++
++#define ASSERT_READ_LOCK(x)                                   \
++do {                                                          \
++      if (x == &request_p_list_udp)                           \
++              MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock);        \
++      else if (x == &request_p_list_tcp)                      \
++              MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock);        \
++} while (0)
++
++#define ASSERT_WRITE_LOCK(x)                                  \
++do {                                                          \
++      if (x == &request_p_list_udp)                           \
++              MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock);       \
++      else if (x == &request_p_list_tcp)                      \
++              MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock);       \
++} while (0)
++
++#include <linux/netfilter_ipv4/listhelp.h>
++
++const int IPT_RPC_CHAR_LEN = 11;
++
++
++static int k_atoi(char *string)
++{
++      unsigned int result = 0;
++      int maxoctet = IPT_RPC_CHAR_LEN;
++
++      for ( ; *string != 0 && maxoctet != 0; maxoctet--, string++) {
++              if (*string < 0)
++                      return(0);
++              if (*string == 0)
++                      break;
++              if (*string < 48 || *string > 57) {
++                      return(0);
++              }
++              result = result * 10 + ( *string - 48 );
++      }
++      return(result);
++}
++
++
++static int match_rpcs(char *c_procs, int i_procs, int proc)
++{
++      int   proc_ctr;
++      char *proc_ptr;
++      unsigned int proc_num;
++
++      DEBUGP("entered match_rpcs [%i] [%i] ..\n", i_procs, proc);
++
++      if (i_procs == -1)
++              return 1;
++
++      for (proc_ctr=0; proc_ctr <= i_procs; proc_ctr++) {
++
++              proc_ptr = c_procs;
++              proc_ptr += proc_ctr * IPT_RPC_CHAR_LEN;
++              proc_num = k_atoi(proc_ptr);
++
++              if (proc_num == proc)
++                      return 1;
++      }
++
++      return 0;
++}
++
++
++static int check_rpc_packet(const u_int32_t *data, const void *matchinfo,
++                      int *hotdrop, int dir, struct ip_conntrack *ct,
++                      int offset, struct list_head request_p_list)
++{
++      const struct ipt_rpc_info *rpcinfo = matchinfo;
++      struct request_p *req_p;
++      u_int32_t xid;
++
++
++      /* Get XID */
++      xid = *data;
++
++      /* This does sanity checking on RPC payloads,
++       * and permits only the RPC "get port" (3)
++       * in authorised procedures in client
++       * communications with the portmapper.
++       */
++
++      data += 5;
++
++      /* Get RPC requestor */
++      if (IXDR_GET_INT32(data) != 3) {
++              DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n");
++              if(rpcinfo->strict == 1)
++                      *hotdrop = 1;
++              return 0;
++      }
++      DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n");
++
++      data++;
++
++      /* Jump Credentials and Verfifier */
++      data = data + IXDR_GET_INT32(data) + 2;
++      data = data + IXDR_GET_INT32(data) + 2;
++
++      /* Get RPC procedure */
++      if (match_rpcs((char *)&rpcinfo->c_procs,
++          rpcinfo->i_procs, IXDR_GET_INT32(data)) == 0) {
++              DEBUGP("RPC packet contains illegal procedure request [%u]. [drop]\n",
++                      (unsigned int)IXDR_GET_INT32(data));
++
++              /* If the RPC conntrack half entry already exists .. */
++
++              switch (ct->tuplehash[0].tuple.dst.protonum) {
++                      case IPPROTO_UDP:
++                              WRITE_LOCK(&ipct_rpc_udp_lock);
++                      case IPPROTO_TCP:
++                              WRITE_LOCK(&ipct_rpc_tcp_lock);
++              }
++              req_p = LIST_FIND(&request_p_list, request_p_cmp,
++                                struct request_p *, xid,
++                                ct->tuplehash[dir].tuple.src.ip,
++                                ct->tuplehash[dir].tuple.src.u.all);
++
++              if (req_p) {
++                      DEBUGP("found req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n",
++                              xid, ct->tuplehash[dir].tuple.dst.protonum,
++                              NIPQUAD(ct->tuplehash[dir].tuple.src.ip),
++                              ntohs(ct->tuplehash[dir].tuple.src.u.all));
++
++                      /* .. remove it */
++                      if (del_timer(&req_p->timeout))
++                              req_p->timeout.expires = 0;
++
++                              LIST_DELETE(&request_p_list, req_p);
++                      DEBUGP("RPC req_p removed. [done]\n");
++
++              } else {
++                      DEBUGP("no req_p found for xid=%u proto=%u %u.%u.%u.%u:%u\n",
++                              xid, ct->tuplehash[dir].tuple.dst.protonum,
++                              NIPQUAD(ct->tuplehash[dir].tuple.src.ip),
++                              ntohs(ct->tuplehash[dir].tuple.src.u.all));
++
++              }
++              switch (ct->tuplehash[0].tuple.dst.protonum) {
++                      case IPPROTO_UDP:
++                              WRITE_UNLOCK(&ipct_rpc_udp_lock);
++                      case IPPROTO_TCP:
++                              WRITE_UNLOCK(&ipct_rpc_tcp_lock);
++              }
++
++              if(rpcinfo->strict == 1)
++                      *hotdrop = 1;
++              return 0;
++      }
++
++      DEBUGP("RPC packet contains authorised procedure request [%u]. [match]\n",
++              (unsigned int)IXDR_GET_INT32(data));
++      return (1 && (!offset));
++}
++
++
++static int match(const struct sk_buff *skb, const struct net_device *in,
++               const struct net_device *out, const void *matchinfo,
++               int offset, const void *hdr, u_int16_t datalen, int *hotdrop)
++{
++      struct ip_conntrack *ct;
++      enum ip_conntrack_info ctinfo;
++      const u_int32_t *data;
++      enum ip_conntrack_dir dir;
++      const struct tcphdr *tcp;
++      const struct ipt_rpc_info *rpcinfo = matchinfo;
++      int port, portsok;
++      int tval;
++
++
++      DEBUGP("new packet to evaluate ..\n");
++
++      ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
++      if (!ct) {
++              DEBUGP("no ct available [skip]\n");
++              return 0;
++      }
++
++      DEBUGP("ct detected. [cont]\n");
++      dir = CTINFO2DIR(ctinfo);
++
++      /* we only want the client to server packets for matching */
++      if (dir != IP_CT_DIR_ORIGINAL)
++              return 0;
++
++      /* This does sanity checking on UDP or TCP packets,
++       * like their respective modules.
++       */
++
++      switch (ct->tuplehash[0].tuple.dst.protonum) {
++
++              case IPPROTO_UDP:
++                      DEBUGP("PROTO_UDP [cont]\n");
++                      if (offset == 0 && datalen < sizeof(struct udphdr)) {
++                              DEBUGP("packet does not contain a complete header. [drop]\n");
++                              return 0;
++                      }
++
++                      for (port=0,portsok=0; port <= ports_n_c; port++) {
++                              if (ntohs(ct->tuplehash[dir].tuple.dst.u.all) == ports[port]) {
++                                      portsok++;
++                                      break;
++                              }
++                      }
++                      if (portsok == 0) {
++                              DEBUGP("packet is not destined for a portmapper [%u]. [skip]\n",
++                                      ntohs(ct->tuplehash[dir].tuple.dst.u.all));
++                              return 0;
++                      }
++
++                      if ((datalen - sizeof(struct udphdr)) != 56) {
++                              DEBUGP("packet length is not correct for RPC content. [skip]\n");
++                              if (rpcinfo->strict == 1)
++                                      *hotdrop = 1;
++                              return 0;
++                      }
++                      DEBUGP("packet length is correct. [cont]\n");
++
++                      /* Get to the data */
++                      data = (const u_int32_t *)hdr + 2;
++
++                      /* Check the RPC data */
++                      tval = check_rpc_packet(data, matchinfo, hotdrop,
++                                              dir, ct, offset,
++                                              request_p_list_udp);
++
++                      return tval;
++                      
++              
++              case IPPROTO_TCP:
++                      DEBUGP("PROTO_TCP [cont]\n");
++                      if (offset == 0 && datalen < sizeof(struct tcphdr)) {
++                              DEBUGP("packet does not contain a complete header. [drop]\n");
++                              return 0;
++                      }
++      
++                      for (port=0,portsok=0; port <= ports_n_c; port++) {
++                              if (ntohs(ct->tuplehash[dir].tuple.dst.u.all) == ports[port]) {
++                                      portsok++;
++                                      break;
++                              }
++                      }
++                      if (portsok == 0) {
++                              DEBUGP("packet is not destined for a portmapper [%u]. [skip]\n",
++                                      ntohs(ct->tuplehash[dir].tuple.dst.u.all));
++                              return 0;
++                      }
++
++                      tcp = hdr;
++                      if (datalen == (tcp->doff * 4)) {
++                              DEBUGP("packet does not contain any data. [match]\n");
++                              return (1 && (!offset));
++                      }
++
++                      /* Tests if packet len is ok */
++                      if ((datalen - (tcp->doff * 4)) != 60) {
++                              DEBUGP("packet length is not correct for RPC content. [skip]\n");
++                              if(rpcinfo->strict == 1)
++                                      *hotdrop = 1;
++                              return 0;
++                      }
++                      DEBUGP("packet length is correct. [cont]\n");
++
++                      /* Get to the data */
++                      data = (const u_int32_t *)tcp + tcp->doff + 1;  
++
++                      /* Check the RPC data */
++                      tval = check_rpc_packet(data, matchinfo, hotdrop,
++                                              dir, ct, offset,
++                                              request_p_list_tcp);
++
++                      return tval;
++
++      }
++
++      DEBUGP("transport protocol=%u, is not supported [skip]\n",
++              ct->tuplehash[0].tuple.dst.protonum);
++      return 0;
++}
++
++
++static int checkentry(const char *tablename, const struct ipt_ip *ip, void *matchinfo,
++                 unsigned int matchsize, unsigned int hook_mask)
++{
++      if (hook_mask
++          & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_FORWARD) | (1 << NF_IP_POST_ROUTING)
++              | (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_LOCAL_OUT))) {
++              printk("ipt_rpc: only valid for PRE_ROUTING, FORWARD, POST_ROUTING, LOCAL_IN and/or LOCAL_OUT targets.\n");
++              return 0;
++      }
++
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_rpc_info)))
++              return 0;
++
++      return 1;
++}
++
++
++static struct ipt_match rpc_match = { { NULL, NULL }, "rpc",
++                                      &match, &checkentry, NULL,
++                                      THIS_MODULE };
++
++
++static int __init init(void)
++{
++      int port;
++
++      DEBUGP("incrementing usage counts\n");
++      __MOD_INC_USE_COUNT(ip_conntrack_rpc_udp);
++      __MOD_INC_USE_COUNT(ip_conntrack_rpc_tcp);
++
++      /* If no port given, default to standard RPC port */
++      if (ports[0] == 0)
++              ports[0] = RPC_PORT;
++
++      DEBUGP("registering match [%s] for;\n", rpc_match.name);
++      for (port = 0; (port < MAX_PORTS) && ports[port]; port++) {
++              DEBUGP("  port %i (UDP|TCP);\n", ports[port]);
++              ports_n_c++;
++      }
++      
++      return ipt_register_match(&rpc_match);
++}
++
++
++static void fini(void)
++{
++      DEBUGP("unregistering match\n");
++      ipt_unregister_match(&rpc_match);
++
++      DEBUGP("decrementing usage counts\n");
++      __MOD_DEC_USE_COUNT(ip_conntrack_rpc_tcp);
++      __MOD_DEC_USE_COUNT(ip_conntrack_rpc_udp);
++}
++
++
++module_init(init);
++module_exit(fini);
++
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_state.c linux-2.4.20/net/ipv4/netfilter/ipt_state.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_state.c    Sun Sep 30 19:26:08 2001
++++ linux-2.4.20/net/ipv4/netfilter/ipt_state.c        Wed Sep 24 09:18:12 2003
+@@ -21,7 +21,9 @@
+       enum ip_conntrack_info ctinfo;
+       unsigned int statebit;
+-      if (!ip_conntrack_get((struct sk_buff *)skb, &ctinfo))
++      if (skb->nfct == &ip_conntrack_untracked.infos[IP_CT_NEW])
++              statebit = IPT_STATE_UNTRACKED;
++      else if (!ip_conntrack_get((struct sk_buff *)skb, &ctinfo))
+               statebit = IPT_STATE_INVALID;
+       else
+               statebit = IPT_STATE_BIT(ctinfo);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_string.c linux-2.4.20/net/ipv4/netfilter/ipt_string.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_string.c   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_string.c       Wed Sep 24 09:18:05 2003
+@@ -0,0 +1,218 @@
++/* Kernel module to match a string into a packet.
++ *
++ * Copyright (C) 2000 Emmanuel Roger  <winfield@freegates.be>
++ * 
++ * ChangeLog
++ *    19.02.2002: Gianni Tedesco <gianni@ecsc.co.uk>
++ *            Fixed SMP re-entrancy problem using per-cpu data areas
++ *            for the skip/shift tables.
++ *    02.05.2001: Gianni Tedesco <gianni@ecsc.co.uk>
++ *            Fixed kernel panic, due to overrunning boyer moore string
++ *            tables. Also slightly tweaked heuristic for deciding what
++ *            search algo to use.
++ *    27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk>
++ *            Implemented Boyer Moore Sublinear search algorithm
++ *            alongside the existing linear search based on memcmp().
++ *            Also a quick check to decide which method to use on a per
++ *            packet basis.
++ */
++
++#include <linux/smp.h>
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/file.h>
++#include <net/sock.h>
++
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_string.h>
++
++MODULE_LICENSE("GPL");
++
++struct string_per_cpu {
++      int *skip;
++      int *shift;
++      int *len;
++};
++
++struct string_per_cpu *bm_string_data=NULL;
++
++/* Boyer Moore Sublinear string search - VERY FAST */
++char *search_sublinear (char *needle, char *haystack, int needle_len, int haystack_len) 
++{
++      int M1, right_end, sk, sh;  
++      int ended, j, i;
++
++      int *skip, *shift, *len;
++      
++      /* use data suitable for this CPU */
++      shift=bm_string_data[smp_processor_id()].shift;
++      skip=bm_string_data[smp_processor_id()].skip;
++      len=bm_string_data[smp_processor_id()].len;
++      
++      /* Setup skip/shift tables */
++      M1 = right_end = needle_len-1;
++      for (i = 0; i < BM_MAX_HLEN; i++) skip[i] = needle_len;  
++      for (i = 0; needle[i]; i++) skip[needle[i]] = M1 - i;  
++
++      for (i = 1; i < needle_len; i++) {   
++              for (j = 0; j < needle_len && needle[M1 - j] == needle[M1 - i - j]; j++);  
++              len[i] = j;  
++      }  
++
++      shift[0] = 1;  
++      for (i = 1; i < needle_len; i++) shift[i] = needle_len;  
++      for (i = M1; i > 0; i--) shift[len[i]] = i;  
++      ended = 0;  
++      
++      for (i = 0; i < needle_len; i++) {  
++              if (len[i] == M1 - i) ended = i;  
++              if (ended) shift[i] = ended;  
++      }  
++
++      /* Do the search*/  
++      while (right_end < haystack_len)
++      {
++              for (i = 0; i < needle_len && haystack[right_end - i] == needle[M1 - i]; i++);  
++              if (i == needle_len) {
++                      return haystack+(right_end - M1);
++              }
++              
++              sk = skip[haystack[right_end - i]];  
++              sh = shift[i];
++              right_end = max(right_end - i + sk, right_end + sh);  
++      }
++
++      return NULL;
++}  
++
++/* Linear string search based on memcmp() */
++char *search_linear (char *needle, char *haystack, int needle_len, int haystack_len) 
++{
++      char *k = haystack + (haystack_len-needle_len);
++      char *t = haystack;
++      
++      while ( t <= k ) {
++              if (memcmp(t, needle, needle_len) == 0)
++                      return t;
++              t++;
++      }
++
++      return NULL;
++}
++
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      const struct ipt_string_info *info = matchinfo;
++      struct iphdr *ip = skb->nh.iph;
++      int hlen, nlen;
++      char *needle, *haystack;
++      proc_ipt_search search=search_linear;
++
++      if ( !ip ) return 0;
++
++      /* get lenghts, and validate them */
++      nlen=info->len;
++      hlen=ntohs(ip->tot_len)-(ip->ihl*4);
++      if ( nlen > hlen ) return 0;
++
++      needle=(char *)&info->string;
++      haystack=(char *)ip+(ip->ihl*4);
++
++      /* The sublinear search comes in to its own
++       * on the larger packets */
++      if ( (hlen>IPT_STRING_HAYSTACK_THRESH) &&
++              (nlen>IPT_STRING_NEEDLE_THRESH) ) {
++              if ( hlen < BM_MAX_HLEN ) {
++                      search=search_sublinear;
++              }else{
++                      if (net_ratelimit())
++                              printk(KERN_INFO "ipt_string: Packet too big "
++                                      "to attempt sublinear string search "
++                                      "(%d bytes)\n", hlen );
++              }
++      }
++      
++    return ((search(needle, haystack, nlen, hlen)!=NULL) ^ info->invert);
++}
++
++static int
++checkentry(const char *tablename,
++           const struct ipt_ip *ip,
++           void *matchinfo,
++           unsigned int matchsize,
++           unsigned int hook_mask)
++{
++
++       if (matchsize != IPT_ALIGN(sizeof(struct ipt_string_info)))
++               return 0;
++
++       return 1;
++}
++
++void string_freeup_data(void)
++{
++      int c;
++      
++      if ( bm_string_data ) {
++              for(c=0; c<smp_num_cpus; c++) {
++                      if ( bm_string_data[c].shift ) kfree(bm_string_data[c].shift);
++                      if ( bm_string_data[c].skip ) kfree(bm_string_data[c].skip);
++                      if ( bm_string_data[c].len ) kfree(bm_string_data[c].len);
++              }
++              kfree(bm_string_data);
++      }
++}
++
++static struct ipt_match string_match
++= { { NULL, NULL }, "string", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      int c;
++      size_t tlen;
++      size_t alen;
++
++      tlen=sizeof(struct string_per_cpu)*smp_num_cpus;
++      alen=sizeof(int)*BM_MAX_HLEN;
++      
++      /* allocate array of structures */
++      if ( !(bm_string_data=kmalloc(tlen,GFP_KERNEL)) ) {
++              return 0;
++      }
++      
++      memset(bm_string_data, 0, tlen);
++      
++      /* allocate our skip/shift tables */
++      for(c=0; c<smp_num_cpus; c++) {
++              if ( !(bm_string_data[c].shift=kmalloc(alen, GFP_KERNEL)) )
++                      goto alloc_fail;
++              if ( !(bm_string_data[c].skip=kmalloc(alen, GFP_KERNEL)) )
++                      goto alloc_fail;
++              if ( !(bm_string_data[c].len=kmalloc(alen, GFP_KERNEL)) )
++                      goto alloc_fail;
++      }
++      
++      return ipt_register_match(&string_match);
++
++alloc_fail:
++      string_freeup_data();
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&string_match);
++      string_freeup_data();
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_time.c linux-2.4.20/net/ipv4/netfilter/ipt_time.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_time.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_time.c Wed Sep 24 09:17:10 2003
+@@ -0,0 +1,185 @@
++/*
++  This is a module which is used for time matching
++  It is using some modified code from dietlibc (localtime() function)
++  that you can find at http://www.fefe.de/dietlibc/
++  This file is distributed under the terms of the GNU General Public
++  License (GPL). Copies of the GPL can be obtained from: ftp://prep.ai.mit.edu/pub/gnu/GPL
++  2001-05-04 Fabrice MARIE <fabrice@netfilter.org> : initial development.
++  2001-21-05 Fabrice MARIE <fabrice@netfilter.org> : bug fix in the match code,
++     thanks to "Zeng Yu" <zengy@capitel.com.cn> for bug report.
++  2001-26-09 Fabrice MARIE <fabrice@netfilter.org> : force the match to be in LOCAL_IN or PRE_ROUTING only.
++  2001-30-11 Fabrice : added the possibility to use the match in FORWARD/OUTPUT with a little hack,
++     added Nguyen Dang Phuoc Dong <dongnd@tlnet.com.vn> patch to support timezones.
++*/
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ipt_time.h>
++#include <linux/time.h>
++
++MODULE_AUTHOR("Fabrice MARIE <fabrice@netfilter.org>");
++MODULE_DESCRIPTION("Match arrival timestamp");
++MODULE_LICENSE("GPL");
++
++struct tm
++{
++      int tm_sec;                   /* Seconds.     [0-60] (1 leap second) */
++      int tm_min;                   /* Minutes.     [0-59] */
++      int tm_hour;                  /* Hours.       [0-23] */
++      int tm_mday;                  /* Day.         [1-31] */
++      int tm_mon;                   /* Month.       [0-11] */
++      int tm_year;                  /* Year - 1900.  */
++      int tm_wday;                  /* Day of week. [0-6] */
++      int tm_yday;                  /* Days in year.[0-365] */
++      int tm_isdst;                 /* DST.         [-1/0/1]*/
++
++      long int tm_gmtoff;           /* we don't care, we count from GMT */
++      const char *tm_zone;          /* we don't care, we count from GMT */
++};
++
++void
++localtime(const time_t *timepr, struct tm *r);
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      const struct ipt_time_info *info = matchinfo;   /* match info for rule */
++      struct tm currenttime;                          /* time human readable */
++      u_int8_t days_of_week[7] = {64, 32, 16, 8, 4, 2, 1};
++      u_int16_t packet_time;
++      struct timeval kerneltimeval;
++      time_t packet_local_time;
++
++      /* if kerneltime=1, we don't read the skb->timestamp but kernel time instead */
++      if (info->kerneltime)
++      {
++              do_gettimeofday(&kerneltimeval);
++              packet_local_time = kerneltimeval.tv_sec;
++      }
++      else
++              packet_local_time = skb->stamp.tv_sec;
++
++      /* Transform the timestamp of the packet, in a human readable form */
++      localtime(&packet_local_time, &currenttime);
++
++      /* check if we match this timestamp, we start by the days... */
++      if ((days_of_week[currenttime.tm_wday] & info->days_match) != days_of_week[currenttime.tm_wday])
++              return 0; /* the day doesn't match */
++
++      /* ... check the time now */
++      packet_time = (currenttime.tm_hour * 60) + currenttime.tm_min;
++      if ((packet_time < info->time_start) || (packet_time > info->time_stop))
++              return 0;
++
++      /* here we match ! */
++      return 1;
++}
++
++static int
++checkentry(const char *tablename,
++           const struct ipt_ip *ip,
++           void *matchinfo,
++           unsigned int matchsize,
++           unsigned int hook_mask)
++{
++      struct ipt_time_info *info = matchinfo;   /* match info for rule */
++
++      /* First, check that we are in the correct hook */
++      /* PRE_ROUTING, LOCAL_IN or FROWARD */
++      if (hook_mask
++            & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT)))
++      {
++              printk("ipt_time: error, only valid for PRE_ROUTING, LOCAL_IN, FORWARD and OUTPUT)\n");
++              return 0;
++      }
++      /* we use the kerneltime if we are in forward or output */
++      info->kerneltime = 1;
++      if (hook_mask & ~((1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))) 
++              /* if not, we use the skb time */
++              info->kerneltime = 0;
++
++      /* Check the size */
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_time_info)))
++              return 0;
++      /* Now check the coherence of the data ... */
++      if ((info->time_start > 1439) ||        /* 23*60+59 = 1439*/
++          (info->time_stop  > 1439))
++      {
++              printk(KERN_WARNING "ipt_time: invalid argument\n");
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ipt_match time_match
++= { { NULL, NULL }, "time", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      printk("ipt_time loading\n");
++      return ipt_register_match(&time_match);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&time_match);
++      printk("ipt_time unloaded\n");
++}
++
++module_init(init);
++module_exit(fini);
++
++
++/* The part below is borowed and modified from dietlibc */
++
++/* seconds per day */
++#define SPD 24*60*60
++
++void
++localtime(const time_t *timepr, struct tm *r) {
++      time_t i;
++      time_t timep;
++      extern struct timezone sys_tz;
++      const unsigned int __spm[12] =
++              { 0,
++                (31),
++                (31+28),
++                (31+28+31),
++                (31+28+31+30),
++                (31+28+31+30+31),
++                (31+28+31+30+31+30),
++                (31+28+31+30+31+30+31),
++                (31+28+31+30+31+30+31+31),
++                (31+28+31+30+31+30+31+31+30),
++                (31+28+31+30+31+30+31+31+30+31),
++                (31+28+31+30+31+30+31+31+30+31+30),
++              };
++      register time_t work;
++
++      timep = (*timepr) - (sys_tz.tz_minuteswest * 60);
++      work=timep%(SPD);
++      r->tm_sec=work%60; work/=60;
++      r->tm_min=work%60; r->tm_hour=work/60;
++      work=timep/(SPD);
++      r->tm_wday=(4+work)%7;
++      for (i=1970; ; ++i) {
++              register time_t k= (!(i%4) && ((i%100) || !(i%400)))?366:365;
++              if (work>k)
++                      work-=k;
++              else
++                      break;
++      }
++      r->tm_year=i-1900;
++      for (i=11; i && __spm[i]>work; --i) ;
++      r->tm_mon=i;
++      r->tm_mday=work-__spm[i]+1;
++}
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_u32.c linux-2.4.20/net/ipv4/netfilter/ipt_u32.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_u32.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/ipt_u32.c  Wed Sep 24 09:17:12 2003
+@@ -0,0 +1,211 @@
++/* Kernel module to match u32 packet content. */
++
++/* 
++U32 tests whether quantities of up to 4 bytes extracted from a packet 
++have specified values.  The specification of what to extract is general 
++enough to find data at given offsets from tcp headers or payloads.
++
++ --u32 tests
++ The argument amounts to a program in a small language described below.
++ tests := location = value |  tests && location = value
++ value := range | value , range
++ range := number | number : number
++  a single number, n, is interpreted the same as n:n
++  n:m is interpreted as the range of numbers >=n and <=m
++ location := number | location operator number
++ operator := & | << | >> | @
++
++ The operators &, <<, >>, && mean the same as in c.  The = is really a set
++ membership operator and the value syntax describes a set.  The @ operator
++ is what allows moving to the next header and is described further below.
++
++ *** Until I can find out how to avoid it, there are some artificial limits
++ on the size of the tests:
++ - no more than 10 ='s (and 9 &&'s) in the u32 argument
++ - no more than 10 ranges (and 9 commas) per value
++ - no more than 10 numbers (and 9 operators) per location
++
++ To describe the meaning of location, imagine the following machine that
++ interprets it.  There are three registers:
++  A is of type char*, initially the address of the IP header
++  B and C are unsigned 32 bit integers, initially zero
++
++  The instructions are:
++   number     B = number;
++              C = (*(A+B)<<24)+(*(A+B+1)<<16)+(*(A+B+2)<<8)+*(A+B+3)
++   &number    C = C&number
++   <<number   C = C<<number
++   >>number   C = C>>number
++   @number    A = A+C; then do the instruction number
++  Any access of memory outside [skb->head,skb->end] causes the match to fail.
++  Otherwise the result of the computation is the final value of C.
++
++ Whitespace is allowed but not required in the tests.
++ However the characters that do occur there are likely to require
++ shell quoting, so it's a good idea to enclose the arguments in quotes.
++
++Example:
++ match IP packets with total length >= 256
++ The IP header contains a total length field in bytes 2-3.
++ --u32 "0&0xFFFF=0x100:0xFFFF" 
++ read bytes 0-3
++ AND that with FFFF (giving bytes 2-3),
++ and test whether that's in the range [0x100:0xFFFF]
++
++Example: (more realistic, hence more complicated)
++ match icmp packets with icmp type 0
++ First test that it's an icmp packet, true iff byte 9 (protocol) = 1
++ --u32 "6&0xFF=1 && ...
++ read bytes 6-9, use & to throw away bytes 6-8 and compare the result to 1
++ Next test that it's not a fragment.
++  (If so it might be part of such a packet but we can't always tell.)
++  n.b. This test is generally needed if you want to match anything
++  beyond the IP header.
++ The last 6 bits of byte 6 and all of byte 7 are 0 iff this is a complete
++ packet (not a fragment).  Alternatively, you can allow first fragments
++ by only testing the last 5 bits of byte 6.
++ ... 4&0x3FFF=0 && ...
++ Last test: the first byte past the IP header (the type) is 0
++ This is where we have to use the @syntax.  The length of the IP header
++ (IHL) in 32 bit words is stored in the right half of byte 0 of the
++ IP header itself.
++ ... 0>>22&0x3C@0>>24=0"
++ The first 0 means read bytes 0-3,
++ >>22 means shift that 22 bits to the right.  Shifting 24 bits would give
++   the first byte, so only 22 bits is four times that plus a few more bits.
++ &3C then eliminates the two extra bits on the right and the first four 
++ bits of the first byte.
++ For instance, if IHL=5 then the IP header is 20 (4 x 5) bytes long.
++ In this case bytes 0-1 are (in binary) xxxx0101 yyzzzzzz, 
++ >>22 gives the 10 bit value xxxx0101yy and &3C gives 010100.
++ @ means to use this number as a new offset into the packet, and read
++ four bytes starting from there.  This is the first 4 bytes of the icmp
++ payload, of which byte 0 is the icmp type.  Therefore we simply shift
++ the value 24 to the right to throw out all but the first byte and compare
++ the result with 0.
++
++Example: 
++ tcp payload bytes 8-12 is any of 1, 2, 5 or 8
++ First we test that the packet is a tcp packet (similar to icmp).
++ --u32 "6&0xFF=6 && ...
++ Next, test that it's not a fragment (same as above).
++ ... 0>>22&0x3C@12>>26&0x3C@8=1,2,5,8"
++ 0>>22&3C as above computes the number of bytes in the IP header.
++ @ makes this the new offset into the packet, which is the start of the
++ tcp header.  The length of the tcp header (again in 32 bit words) is
++ the left half of byte 12 of the tcp header.  The 12>>26&3C
++ computes this length in bytes (similar to the IP header before).
++ @ makes this the new offset, which is the start of the tcp payload.
++ Finally 8 reads bytes 8-12 of the payload and = checks whether the
++ result is any of 1, 2, 5 or 8
++*/
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++
++#include <linux/netfilter_ipv4/ipt_u32.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++/* #include <asm-i386/timex.h> for timing */
++
++MODULE_AUTHOR("Don Cohen <don@isis.cs3-inc.com>");
++MODULE_DESCRIPTION("IP tables u32 matching module");
++MODULE_LICENSE("GPL");
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *hdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      const struct ipt_u32 *data = matchinfo;
++      int testind, i;
++      unsigned char* origbase = (char*)skb->nh.iph;
++      unsigned char* base = origbase;
++      unsigned char* head = skb->head;
++      unsigned char* end = skb->end;
++      int nnums, nvals;
++      u_int32_t pos, val;
++      /* unsigned long long cycles1, cycles2, cycles3, cycles4;
++         cycles1 = get_cycles(); */
++
++      for (testind=0; testind < data->ntests; testind++) {
++              base = origbase; /* reset for each test */
++              pos = data->tests[testind].location[0].number;
++              if (base+pos+3 > end || base+pos < head) 
++                      return 0;
++              val = (base[pos]<<24) + (base[pos+1]<<16) +
++                      (base[pos+2]<<8) + base[pos+3];
++              nnums = data->tests[testind].nnums;
++              for (i=1; i < nnums; i++) {
++                      u_int32_t number = data->tests[testind].location[i].number;
++                      switch (data->tests[testind].location[i].nextop) {
++                      case IPT_U32_AND: 
++                              val = val & number; 
++                              break;
++                      case IPT_U32_LEFTSH: 
++                              val = val << number;
++                              break;
++                      case IPT_U32_RIGHTSH: 
++                              val = val >> number; 
++                              break;
++                      case IPT_U32_AT:
++                              base = base + val;
++                              pos = number;
++                              if (base+pos+3 > end || base+pos < head) 
++                                      return 0;
++                              val = (base[pos]<<24) + (base[pos+1]<<16) +
++                                      (base[pos+2]<<8) + base[pos+3];
++                              break;
++                      }
++              }
++              nvals = data->tests[testind].nvalues;
++              for (i=0; i < nvals; i++) {
++                      if ((data->tests[testind].value[i].min <= val) &&
++                          (val <= data->tests[testind].value[i].max)) {
++                              break;
++                      }
++              }
++              if (i >= data->tests[testind].nvalues) {
++                      /* cycles2 = get_cycles(); 
++                         printk("failed %d in %d cycles\n", testind, 
++                                cycles2-cycles1); */
++                      return 0;
++              }
++      }
++      /* cycles2 = get_cycles();
++         printk("succeeded in %d cycles\n", cycles2-cycles1); */
++      return 1;
++}
++
++static int
++checkentry(const char *tablename,
++           const struct ipt_ip *ip,
++           void *matchinfo,
++           unsigned int matchsize,
++           unsigned int hook_mask)
++{
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_u32)))
++              return 0;
++      return 1;
++}
++
++static struct ipt_match u32_match
++= { { NULL, NULL }, "u32", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      return ipt_register_match(&u32_match);
++}
++
++static void __exit fini(void)
++{
++      ipt_unregister_match(&u32_match);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/ipt_unclean.c linux-2.4.20/net/ipv4/netfilter/ipt_unclean.c
+--- linux-2.4.20.org/net/ipv4/netfilter/ipt_unclean.c  Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv4/netfilter/ipt_unclean.c      Wed Sep 24 09:16:24 2003
+@@ -259,6 +259,24 @@
+ #define       TH_ECE  0x40
+ #define       TH_CWR  0x80
++/* table of valid flag combinations - ECE and CWR are always valid */
++static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] =
++{
++      [TH_SYN]                        = 1,
++      [TH_SYN|TH_ACK]                 = 1,
++      [TH_RST]                        = 1,
++      [TH_RST|TH_ACK]                 = 1,
++      [TH_RST|TH_ACK|TH_PUSH]         = 1,
++      [TH_FIN|TH_ACK]                 = 1,
++      [TH_ACK]                        = 1,
++      [TH_ACK|TH_PUSH]                = 1,
++      [TH_ACK|TH_URG]                 = 1,
++      [TH_ACK|TH_URG|TH_PUSH]         = 1,
++      [TH_FIN|TH_ACK|TH_PUSH]         = 1,
++      [TH_FIN|TH_ACK|TH_URG]          = 1,
++      [TH_FIN|TH_ACK|TH_URG|TH_PUSH]  = 1
++};
++
+ /* TCP-specific checks. */
+ static int
+ check_tcp(const struct iphdr *iph,
+@@ -330,19 +348,7 @@
+       /* CHECK: TCP flags. */
+       tcpflags = (((u_int8_t *)tcph)[13] & ~(TH_ECE|TH_CWR));
+-      if (tcpflags != TH_SYN
+-          && tcpflags != (TH_SYN|TH_ACK)
+-              && tcpflags != TH_RST
+-          && tcpflags != (TH_RST|TH_ACK)
+-          && tcpflags != (TH_RST|TH_ACK|TH_PUSH)
+-          && tcpflags != (TH_FIN|TH_ACK)
+-          && tcpflags != TH_ACK
+-          && tcpflags != (TH_ACK|TH_PUSH)
+-          && tcpflags != (TH_ACK|TH_URG)
+-          && tcpflags != (TH_ACK|TH_URG|TH_PUSH)
+-          && tcpflags != (TH_FIN|TH_ACK|TH_PUSH)
+-          && tcpflags != (TH_FIN|TH_ACK|TH_URG)
+-          && tcpflags != (TH_FIN|TH_ACK|TH_URG|TH_PUSH)) {
++      if (!tcp_valid_flags[tcpflags]) {
+               limpk("TCP flags bad: %u\n", tcpflags);
+               return 0;
+       }
+@@ -521,6 +527,16 @@
+               return 0;
+       }
++      /* CHECK: Do not use what is unused.
++       * First bit of fragmentation flags should be unused.
++       * May be used by OS fingerprinting tools.
++       * 04 Jun 2002, Maciej Soltysiak, solt@dns.toxicfilms.tv
++       */
++      if (ntohs(iph->frag_off)>>15) {
++              limpk("IP unused bit set\n");
++              return 0;
++      }
++
+       /* Per-protocol checks. */
+       switch (iph->protocol) {
+       case IPPROTO_ICMP:
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/iptable_mangle.c linux-2.4.20/net/ipv4/netfilter/iptable_mangle.c
+--- linux-2.4.20.org/net/ipv4/netfilter/iptable_mangle.c       Mon Feb 25 19:38:14 2002
++++ linux-2.4.20/net/ipv4/netfilter/iptable_mangle.c   Wed Sep 24 09:16:14 2003
+@@ -170,7 +170,7 @@
+ static struct nf_hook_ops ipt_ops[]
+ = { { { NULL, NULL }, ipt_route_hook, PF_INET, NF_IP_PRE_ROUTING, 
+       NF_IP_PRI_MANGLE },
+-    { { NULL, NULL }, ipt_local_hook, PF_INET, NF_IP_LOCAL_IN,
++    { { NULL, NULL }, ipt_route_hook, PF_INET, NF_IP_LOCAL_IN,
+       NF_IP_PRI_MANGLE },
+     { { NULL, NULL }, ipt_route_hook, PF_INET, NF_IP_FORWARD,
+       NF_IP_PRI_MANGLE },
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/iptable_raw.c linux-2.4.20/net/ipv4/netfilter/iptable_raw.c
+--- linux-2.4.20.org/net/ipv4/netfilter/iptable_raw.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/iptable_raw.c      Wed Sep 24 09:18:12 2003
+@@ -0,0 +1,151 @@
++/* 
++ * 'raw' table, which is the very first hooked in at PRE_ROUTING and LOCAL_OUT .
++ *
++ * Copyright (C) 2003 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
++ */
++#include <linux/module.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++#define RAW_VALID_HOOKS ((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT))
++
++/* Standard entry. */
++struct ipt_standard
++{
++      struct ipt_entry entry;
++      struct ipt_standard_target target;
++};
++
++struct ipt_error_target
++{
++      struct ipt_entry_target target;
++      char errorname[IPT_FUNCTION_MAXNAMELEN];
++};
++
++struct ipt_error
++{
++      struct ipt_entry entry;
++      struct ipt_error_target target;
++};
++
++static struct
++{
++      struct ipt_replace repl;
++      struct ipt_standard entries[2];
++      struct ipt_error term;
++} initial_table __initdata
++= { { "raw", RAW_VALID_HOOKS, 3,
++      sizeof(struct ipt_standard) * 2 + sizeof(struct ipt_error),
++      { [NF_IP_PRE_ROUTING] 0,
++      [NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) },
++      { [NF_IP_PRE_ROUTING] 0,
++      [NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) },
++      0, NULL, { } },
++    {
++          /* PRE_ROUTING */
++          { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
++              0,
++              sizeof(struct ipt_entry),
++              sizeof(struct ipt_standard),
++              0, { 0, 0 }, { } },
++            { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
++              -NF_ACCEPT - 1 } },
++          /* LOCAL_OUT */
++          { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
++              0,
++              sizeof(struct ipt_entry),
++              sizeof(struct ipt_standard),
++              0, { 0, 0 }, { } },
++            { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
++              -NF_ACCEPT - 1 } }
++    },
++    /* ERROR */
++    { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
++      0,
++      sizeof(struct ipt_entry),
++      sizeof(struct ipt_error),
++      0, { 0, 0 }, { } },
++      { { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } },
++        { } },
++      "ERROR"
++      }
++    }
++};
++
++static struct ipt_table packet_raw = { 
++      .list = { NULL, NULL }, 
++      .name = "raw", 
++      .table = &initial_table.repl,
++      .valid_hooks =  RAW_VALID_HOOKS, 
++      .lock = RW_LOCK_UNLOCKED, 
++      .private = NULL, 
++      .me = THIS_MODULE
++};
++
++/* The work comes in here from netfilter.c. */
++static unsigned int
++ipt_hook(unsigned int hook,
++       struct sk_buff **pskb,
++       const struct net_device *in,
++       const struct net_device *out,
++       int (*okfn)(struct sk_buff *))
++{
++      return ipt_do_table(pskb, hook, in, out, &packet_raw, NULL);
++}
++
++/* 'raw' is the very first table. */
++static struct nf_hook_ops ipt_ops[] = {
++      /* PRE_ROUTING hook */
++      { .list = { NULL, NULL }, 
++        .hook = ipt_hook, 
++        .pf = PF_INET, 
++        .hooknum = NF_IP_PRE_ROUTING, 
++        .priority = NF_IP_PRI_FIRST },
++      /* LOCAL_OUT hook */
++      { .list = { NULL, NULL }, 
++        .hook = ipt_hook, 
++        .pf = PF_INET, 
++        .hooknum = NF_IP_LOCAL_OUT, 
++        .priority = NF_IP_PRI_FIRST }
++};
++
++static int __init init(void)
++{
++      int ret;
++
++      /* Register table */
++      ret = ipt_register_table(&packet_raw);
++      if (ret < 0)
++              return ret;
++
++      /* Register hooks */
++      ret = nf_register_hook(&ipt_ops[0]);
++      if (ret < 0)
++              goto cleanup_table;
++
++      ret = nf_register_hook(&ipt_ops[1]);
++      if (ret < 0)
++              goto cleanup_hook0;
++
++      return ret;
++
++ cleanup_hook0:
++      nf_unregister_hook(&ipt_ops[0]);
++ cleanup_table:
++      ipt_unregister_table(&packet_raw);
++
++      return ret;
++}
++
++static void __exit fini(void)
++{
++      unsigned int i;
++
++      for (i = 0; i < sizeof(ipt_ops)/sizeof(struct nf_hook_ops); i++)
++              nf_unregister_hook(&ipt_ops[i]);
++
++      ipt_unregister_table(&packet_raw);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/nfnetlink.c linux-2.4.20/net/ipv4/netfilter/nfnetlink.c
+--- linux-2.4.20.org/net/ipv4/netfilter/nfnetlink.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/nfnetlink.c        Wed Sep 24 09:17:51 2003
+@@ -0,0 +1,344 @@
++/* Netfilter messages via netlink socket. Allows for user space
++ * protocol helpers and general trouble making from userspace.
++ *
++ * (C) 2001 by Jay Schulist <jschlst@samba.org>,
++ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
++ *
++ * Initial netfilter messages via netlink development funded and
++ * generally made possible by Network Robots, Inc. (www.networkrobots.com)
++ *
++ * Further development of this code funded by Astaro AG (http://www.astaro.com)
++ *
++ * This software may be used and distributed according to the terms
++ * of the GNU General Public License, incorporated herein by reference.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/socket.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/string.h>
++#include <linux/sockios.h>
++#include <linux/net.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <net/sock.h>
++#include <linux/init.h>
++#include <linux/spinlock.h>
++#include <linux/list.h>
++
++#include <linux/netfilter.h>
++#include <linux/netlink.h>
++#include <linux/nfnetlink.h>
++
++MODULE_LICENSE("GPL");
++
++char nfversion[] = "0.11";
++int nf_debug_level = 1;
++#define nf_debug(level, format, arg...) \
++        if(nf_debug_level > level)  \
++                printk(__FUNCTION__ ": " format, ## arg)
++
++static struct sock *nfnl = NULL;
++static LIST_HEAD(subsys_list);
++static struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
++DECLARE_MUTEX(nfnl_sem);
++
++void nfnl_lock(void)
++{
++      nfnl_shlock();
++      nfnl_exlock();
++}
++
++void nfnl_unlock(void)
++{
++      nfnl_exunlock();
++      nfnl_shunlock();
++}
++
++struct nfnetlink_subsystem *nfnetlink_subsys_alloc(int cb_count)
++{
++      int size;
++      struct nfnetlink_subsystem *ss;
++
++      size = sizeof(struct nfnetlink_subsystem)
++              + (cb_count * sizeof(struct nfnl_callback));
++
++      ss = kmalloc(size, GFP_KERNEL);
++      if (!ss)
++              return NULL;
++      memset(ss, 0, size);
++
++      return ss;
++}
++
++int nfnetlink_subsys_register(struct nfnetlink_subsystem *n)
++{
++      MOD_INC_USE_COUNT;
++
++      nf_debug(0, "registering subsystem ID %u\n", n->subsys_id);
++
++      nfnl_lock();
++      list_add(&n->list, &subsys_list);
++      subsys_table[n->subsys_id] = n;
++      nfnl_unlock();
++
++      return 0;
++}
++
++int nfnetlink_subsys_unregister(struct nfnetlink_subsystem *n)
++{
++      nf_debug(0, "unregistering subsystem ID %u\n", n->subsys_id);
++
++      nfnl_lock();
++      subsys_table[n->subsys_id] = NULL;
++      list_del(&n->list);
++      nfnl_unlock();
++
++      MOD_DEC_USE_COUNT;
++
++      return 0;
++}
++
++struct nfnl_callback *nfnetlink_find_client(u_int16_t nlmsg_type)
++{
++      struct nfnetlink_subsystem *ss;
++      u_int8_t subsys_id = NFNL_SUBSYS_ID(nlmsg_type);
++      u_int8_t type = NFNL_MSG_TYPE(nlmsg_type);
++
++      if (subsys_id >= NFNL_SUBSYS_COUNT
++          || subsys_table[subsys_id] == NULL)
++              return NULL;
++
++      ss = subsys_table[subsys_id];
++
++      if (type >= ss->cb_count) {
++              nf_debug(0, "msgtype %u >= %u, returning\n", type, 
++                       ss->cb_count);
++              return NULL;
++      }
++
++      return &ss->cb[type];
++}
++
++void __nfa_fill(struct sk_buff *skb, int attrtype, int attrlen,
++              const void *data)
++{
++      struct nfattr *nfa;
++      int size = NFA_LENGTH(attrlen);
++
++      nfa = (struct nfattr *)skb_put(skb, NFA_ALIGN(size));
++      nfa->nfa_type = attrtype;
++      nfa->nfa_len  = size;
++      memcpy(NFA_DATA(nfa), data, attrlen);
++}
++
++int nfattr_parse(struct nfattr *tb[], int maxattr, struct nfattr *nfa, int len)
++{
++      memset(tb, 0, sizeof(struct nfattr *)*maxattr);
++
++      while (NFA_OK(nfa, len)) {
++              unsigned flavor = nfa->nfa_type;
++              if (flavor && flavor <= maxattr)
++                      tb[flavor-1] = nfa;
++              nfa = NFA_NEXT(nfa, len);
++      }
++
++      return 0;
++}
++
++/**
++ * nfnetlink_check_attributes - check and parse nfnetlink attributes
++ *
++ * subsys: nfnl subsystem for which this message is to be parsed
++ * nlmsghdr: netlink message to be checked/parsed
++ * cda: array of pointers, needs to be at least subsys->attr_count big
++ *
++ */
++int
++nfnetlink_check_attributes(struct nfnetlink_subsystem *subsys,
++                         struct nlmsghdr *nlh, struct nfattr *cda[])
++{
++      int min_len;
++
++      /* check attribute lengths. */
++      min_len = sizeof(struct nfgenmsg);
++      if (nlh->nlmsg_len < min_len)
++              return -EINVAL;
++
++      if (nlh->nlmsg_len > min_len) {
++              struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
++              int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
++
++              while (NFA_OK(attr, attrlen)) {
++                      unsigned flavor = attr->nfa_type;
++                      if (flavor) {
++                              if (flavor > subsys->attr_count)
++                                      return -EINVAL;
++                              cda[flavor - 1] = attr;
++                      }
++                      attr = NFA_NEXT(attr, attrlen);
++              }
++      } else
++              return -EINVAL;
++
++        return 0;
++}
++
++int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo)
++{
++      int err = 0;
++
++      NETLINK_CB(skb).dst_groups = group;
++      if (echo)
++              atomic_inc(&skb->users);
++      netlink_broadcast(nfnl, skb, pid, group, GFP_KERNEL);
++      if (echo)
++              err = netlink_unicast(nfnl, skb, pid, MSG_DONTWAIT);
++
++      return err;
++}
++
++/* Process one complete nfnetlink message. */
++static inline int nfnetlink_rcv_msg(struct sk_buff *skb,
++                                  struct nlmsghdr *nlh, int *errp)
++{
++      struct nfnl_callback *nc;
++      int type, err = 0;
++
++      nf_debug(0, "entered; subsys=%u, msgtype=%u\n",
++               NFNL_SUBSYS_ID(nlh->nlmsg_type),
++               NFNL_MSG_TYPE(nlh->nlmsg_type));
++
++      /* Only requests are handled by kernel now. */
++      if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
++              nf_debug(0, "received non-request message\n");
++              return 0;
++      }
++
++      /* Unknown message: reply with EINVAL */
++      type = nlh->nlmsg_type;
++      if (NFNL_SUBSYS_ID(type) > NFNL_SUBSYS_COUNT) {
++              nf_debug(0, "subsys_id > subsys_count\n");
++              goto err_inval;
++      }
++
++      /* All the messages must have at least 1 byte length */
++      if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nfgenmsg))) {
++              nf_debug(0, "received message was too short\n");
++              return 0;
++      }
++
++      nc = nfnetlink_find_client(type);
++      if (!nc) {
++              nf_debug(0, "unable to find client for type %d\n", type);
++              goto err_inval;
++      }
++
++      if (nc->cap_required && 
++          !cap_raised(NETLINK_CB(skb).eff_cap, nc->cap_required)) {
++              nf_debug(0, "permission denied for type %d\n", type);
++              *errp = -EPERM;
++              return -1;
++      }
++
++      err = nc->call(nfnl, skb, nlh, errp);
++      *errp = err;
++      return err;
++
++err_inval:
++      *errp = -EINVAL;
++      return -1;
++}
++
++/* Process one packet of messages. */
++static inline int nfnetlink_rcv_skb(struct sk_buff *skb)
++{
++      int err;
++      struct nlmsghdr *nlh;
++
++      while (skb->len >= NLMSG_SPACE(0)) {
++              u32 rlen;
++
++              nlh = (struct nlmsghdr *)skb->data;
++              if (nlh->nlmsg_len < sizeof(struct nlmsghdr)
++                  || skb->len < nlh->nlmsg_len)
++                      return 0;
++              rlen = NLMSG_ALIGN(nlh->nlmsg_len);
++              if (rlen > skb->len)
++                      rlen = skb->len;
++              if (nfnetlink_rcv_msg(skb, nlh, &err)) {
++                      if (!err)
++                              return -1;
++                      netlink_ack(skb, nlh, err);
++              } else
++                      if (nlh->nlmsg_flags & NLM_F_ACK)
++                              netlink_ack(skb, nlh, 0);
++              skb_pull(skb, rlen);
++      }
++
++      return 0;
++}
++
++static void nfnetlink_rcv(struct sock *sk, int len)
++{
++      do {
++              struct sk_buff *skb;
++
++              if (nfnl_shlock_nowait())
++                      return;
++
++              while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
++                      if (nfnetlink_rcv_skb(skb)) {
++                              if (skb->len)
++                                      skb_queue_head(&sk->receive_queue, skb);
++                              else
++                                      kfree_skb(skb);
++                              break;
++                      }
++                      kfree_skb(skb);
++              }
++
++              up(&nfnl_sem);
++      } while(nfnl && nfnl->receive_queue.qlen);
++}
++
++void __exit nfnetlink_exit(void)
++{
++      printk("Netfilter removing netlink socket.\n");
++      sock_release(nfnl->socket);
++      return;
++}
++
++int __init nfnetlink_init(void)
++{
++      int i;
++      printk("Netfilter messages via NETLINK v%s.\n", nfversion);
++
++      for (i = 0; i < NFNL_SUBSYS_COUNT; i++)
++              subsys_table[i] = NULL;
++
++      nfnl = netlink_kernel_create(NETLINK_NETFILTER, nfnetlink_rcv);
++      if (!nfnl) {
++              printk(KERN_ERR "cannot initialize nfnetlink!\n");
++              return -1;
++      }
++
++      return 0;
++}
++
++module_init(nfnetlink_init);
++module_exit(nfnetlink_exit);
++
++EXPORT_SYMBOL_GPL(nfnetlink_subsys_alloc);
++EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
++EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
++EXPORT_SYMBOL_GPL(nfnetlink_check_attributes);
++EXPORT_SYMBOL_GPL(nfnetlink_send);
++EXPORT_SYMBOL_GPL(__nfa_fill);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv4/netfilter/nfnetlink_conntrack.c linux-2.4.20/net/ipv4/netfilter/nfnetlink_conntrack.c
+--- linux-2.4.20.org/net/ipv4/netfilter/nfnetlink_conntrack.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv4/netfilter/nfnetlink_conntrack.c      Wed Sep 24 09:17:51 2003
+@@ -0,0 +1,715 @@
++/* Connection tracking via netlink socket. Allows for user space
++ * protocol helpers and general trouble making from userspace.
++ *
++ * (C) 2001 by Jay Schulist <jschlst@samba.org>
++ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
++ *
++ * Initial connection tracking via netlink development funded and 
++ * generally made possible by Network Robots, Inc. (www.networkrobots.com)
++ *
++ * Further development of this code funded by Astaro AG (http://www.asaro.com)
++ *
++ * This software may be used and distributed according to the terms
++ * of the GNU General Public License, incorporated herein by reference.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/socket.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/string.h>
++#include <linux/sockios.h>
++#include <linux/net.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <net/sock.h>
++#include <linux/init.h>
++#include <linux/netlink.h>
++#include <linux/spinlock.h>
++#include <linux/rtnetlink.h>
++
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++#include <linux/netfilter_ipv4/ip_conntrack_core.h>
++
++#include <linux/nfnetlink.h>
++#include <linux/nfnetlink_conntrack.h>
++
++#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
++#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++MODULE_LICENSE("GPL");
++
++char ctversion[] = "0.11";
++int ct_debug_level = 1;
++#define ct_debug(level, format, arg...) \
++      if(ct_debug_level > level)  \
++              printk(__FUNCTION__ ": " format, ## arg)
++
++/* FIXME: this define is just needed for DUMP_TUPLE */
++#define DEBUGP(format, args...)       ct_debug(0, format, ## args)
++
++static struct nfnetlink_subsystem *ctnl_subsys;
++
++static int
++ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
++                  int event, 
++                  int nowait, 
++                  const struct ip_conntrack *ct, 
++                  const enum ip_conntrack_info *ctinfo, 
++                  unsigned char proto,
++                  const struct net_device *in,
++                  const struct net_device *out)
++{
++      struct nlmsghdr *nlh;
++      struct nfgenmsg *nfmsg;
++      struct cta_proto cp;
++      unsigned long s;
++      unsigned char *b;
++
++      b = skb->tail;
++      nlh = NLMSG_PUT(skb, pid, seq, (NFNL_SUBSYS_CTNETLINK<<8)|event, 
++                      sizeof(struct nfgenmsg));
++      nfmsg = NLMSG_DATA(nlh);
++
++      nlh->nlmsg_flags        = (nowait && pid) ? NLM_F_MULTI : 0;
++      nfmsg->nfgen_family     = AF_INET;
++      NFA_PUT(skb, CTA_ORIG, sizeof(struct ip_conntrack_tuple), 
++              &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++      NFA_PUT(skb, CTA_RPLY, sizeof(struct ip_conntrack_tuple),
++              &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
++      s = ct->status;
++      NFA_PUT(skb, CTA_STATUS, sizeof(unsigned long), &s);
++      if (in)
++              NFA_PUT(skb, CTA_IIF, IFNAMSIZ, in->name);
++      if (out)
++              NFA_PUT(skb, CTA_OIF, IFNAMSIZ, out->name);
++      if (ctinfo)
++              NFA_PUT(skb, CTA_INFO, sizeof(unsigned long), ctinfo);
++
++      cp.num_proto = proto;
++      memcpy(&cp.proto, &ct->proto, sizeof (cp.proto));
++      NFA_PUT(skb, CTA_PROTOINFO, sizeof(cp), &cp);
++
++      if (ct->helper) {
++              struct cta_help ch;
++
++              memcpy(&ch.tuple, &ct->helper->tuple, 
++                      sizeof(struct ip_conntrack_tuple));
++              memcpy(&ch.mask, &ct->helper->mask,
++                      sizeof(struct ip_conntrack_tuple));
++              strncpy((char *)&ch.name, ct->helper->name, sizeof(ch.name));
++              memcpy(&ch.help, &ct->help, sizeof(ch.help));
++              NFA_PUT(skb, CTA_HELPINFO, sizeof(ch), &ch);
++      }
++
++#ifdef CONFIG_IP_NF_NAT_NEEDED
++      if (ct->nat.info.initialized && ct->nat.info.num_manips) {
++              const struct ip_nat_info *nat = &ct->nat.info;
++              struct cta_nat cn;
++
++              cn.num_manips = nat->num_manips;
++              memcpy(&cn.manips, &nat->manips, (nat->num_manips 
++                      * sizeof(struct ip_nat_info_manip)));
++              NFA_PUT(skb, CTA_NATINFO, sizeof(struct cta_nat), &cn);
++      }
++#endif /* CONFIG_IP_NF_NAT_NEEDED */
++
++      nlh->nlmsg_len = skb->tail - b;
++      return skb->len;
++
++nlmsg_failure:
++nfattr_failure:
++      skb_trim(skb, b - skb->data);
++      return -1;
++}
++
++static inline struct sk_buff *
++ctnetlink_event_build_msg(const struct ip_conntrack *ct, 
++                        const enum ip_conntrack_info ctinfo, 
++                        const unsigned char proto,
++                        const struct net_device *in,
++                        const struct net_device *out)
++{
++      struct sk_buff *skb;
++      int err;
++
++      skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
++      if (!skb)
++              return NULL;
++
++      err = ctnetlink_fill_info(skb, 0, 0, CTNL_MSG_NEWCONNTRACK, 1, ct,
++                                &ctinfo, proto, in, out);
++      if (err <= 0)
++              goto nlmsg_failure;
++      return skb;
++
++nlmsg_failure:
++      return NULL;
++}
++
++static void
++ctnetlink_create(struct ip_conntrack *ct,
++               enum ip_conntrack_info ctinfo,
++               const struct net_device *in, 
++               const struct net_device *out)
++{
++      u16 proto = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
++      struct sk_buff *skb;
++
++      skb = ctnetlink_event_build_msg(ct, ctinfo, proto, in, out);
++      if (!skb)
++              return;
++
++      if (proto == IPPROTO_TCP) {
++              nfnetlink_send(skb, 0, NFGRP_IPV4_CT_TCP, 0);
++              return;
++              } else if (proto == IPPROTO_UDP) {
++              nfnetlink_send(skb, 0, NFGRP_IPV4_CT_UDP, 0);
++              return;
++              } else if (proto == IPPROTO_ICMP) {
++              nfnetlink_send(skb, 0, NFGRP_IPV4_CT_ICMP, 0);
++              return;
++      } else {
++              nfnetlink_send(skb, 0, NFGRP_IPV4_CT_OTHER, 0);
++              return;
++      }
++      kfree_skb(skb);
++      return;
++}
++
++#if 0
++static void ctnetlink_destroy(struct ip_conntrack *ct)
++{
++        ctnetlink_create(ct, IP_CT_DELETE, NULL, NULL);
++}
++#endif
++
++static inline int ctnetlink_kill(const struct ip_conntrack *i, void *data)
++{
++      struct ip_conntrack *t = (struct ip_conntrack *)data;
++
++      if (!memcmp(&i->tuplehash[IP_CT_DIR_ORIGINAL], 
++          &t->tuplehash[IP_CT_DIR_ORIGINAL], 
++          sizeof(struct ip_conntrack_tuple_hash))) {
++              //ip_conntrack_put(t);
++              nf_conntrack_put(&t->infos[0]);
++              return 1;
++      }
++
++      return 0;
++}
++
++static int
++ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, 
++                      struct nlmsghdr *nlh, int *errp)
++{
++        struct ip_conntrack_tuple_hash *h;
++      struct ip_conntrack_tuple *tuple;
++      struct nfattr *cda[CTA_MAX];
++
++      ct_debug(0, "entered\n");
++
++      if (nfnetlink_check_attributes(ctnl_subsys, nlh, cda) < 0)
++              return -EINVAL;
++
++      if (cda[CTA_ORIG-1])
++              tuple = NFA_DATA(cda[CTA_ORIG-1]);
++      else {
++              if (cda[CTA_RPLY-1])
++                      tuple = NFA_DATA(cda[CTA_RPLY-1]);
++              else {
++                      ct_debug(0, "no tuple found in request\n");
++                      return -EINVAL;
++              }
++      }
++
++      h = ip_conntrack_find_get(tuple, NULL);
++        if (!h) {
++              ct_debug(0, "tuple not found in conntrack hash:");
++              DUMP_TUPLE(tuple);
++              return -ENOENT;
++      }
++
++      ct_debug(0, "calling selective_cleanup\n");
++      ip_ct_selective_cleanup(ctnetlink_kill, h->ctrack);
++
++      return 0;
++}
++
++static int ctnetlink_done(struct netlink_callback *cb)
++{
++      ct_debug(0, "entering\n");
++        return 0;
++}
++
++static int
++ctnetlink_dump_build_msg(const struct ip_conntrack_tuple_hash *hash,
++                       struct sk_buff *skb, u32 pid, u32 seq)
++{
++      struct ip_conntrack *ct;
++      int err, proto;
++
++      /* Only count originals */
++      if (DIRECTION(hash))
++              return 0;
++
++      ct = hash->ctrack;
++      if (!ct)
++              goto nlmsg_failure;
++
++      proto = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
++      err = ctnetlink_fill_info(skb, pid, seq, CTNL_MSG_NEWCONNTRACK, 1,
++                                ct, NULL, proto, NULL, NULL);
++      if (err <= 0)
++              goto nlmsg_failure;
++      return 0;
++
++nlmsg_failure:
++      if (skb)
++              kfree_skb(skb);
++      return -1;
++}
++
++static int
++ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
++{
++      int i;
++      int idx;
++      int s_idx = cb->args[0];
++
++      /* Traverse hash; send originals then reply. */
++      READ_LOCK(&ip_conntrack_lock);
++        for (i = 0, idx = 0; i < ip_conntrack_htable_size; i++, idx++) {
++              if (idx < s_idx)
++                      continue;
++              if (LIST_FIND(&ip_conntrack_hash[i], ctnetlink_dump_build_msg,
++                            struct ip_conntrack_tuple_hash *, skb,
++                            NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq))
++                      continue;
++      }
++      READ_UNLOCK(&ip_conntrack_lock);
++
++      cb->args[0] = idx;
++      return skb->len;
++}
++
++static int
++ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, 
++                      struct nlmsghdr *nlh, int *errp)
++{
++      struct ip_conntrack_tuple_hash *h;
++      struct ip_conntrack_tuple *tuple;
++      struct nfattr *cda[CTA_MAX];
++      struct ip_conntrack *ct;
++      struct sk_buff *skb2 = NULL;
++      int err, proto;
++
++      ct_debug(0, "entered\n");
++
++      if (nlh->nlmsg_flags & NLM_F_DUMP) {
++              struct nfgenmsg *msg = NLMSG_DATA(nlh);
++              u32 rlen;
++
++              if (msg->nfgen_family != AF_INET)
++                      return -EAFNOSUPPORT;
++
++              ct_debug(0, "starting dump\n");
++                if ((*errp = netlink_dump_start(ctnl, skb, nlh,
++                                              ctnetlink_dump_table,
++                                              ctnetlink_done)) != 0)
++                      return -EINVAL;
++              rlen = NLMSG_ALIGN(nlh->nlmsg_len);
++              if (rlen > skb->len)
++                      rlen = skb->len;
++              skb_pull(skb, rlen);
++              return 0;
++      }
++
++      if (nfnetlink_check_attributes(ctnl_subsys, nlh, cda) < 0)
++              return -EINVAL;
++
++      if (cda[CTA_ORIG-1])
++              tuple = NFA_DATA(cda[CTA_ORIG-1]);
++      else {
++              if (cda[CTA_RPLY-1])
++                      tuple = NFA_DATA(cda[CTA_RPLY-1]);
++              else
++                      return -EINVAL;
++      }
++
++      h = ip_conntrack_find_get(tuple, NULL);
++      if (!h)
++              return -ENOENT;
++
++      ct = h->ctrack;
++      if (!ct)
++              goto nlmsg_failure;
++
++      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
++      if (!skb2)
++              return -ENOMEM;
++      NETLINK_CB(skb2).dst_pid = NETLINK_CB(skb).pid;
++
++        proto = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
++      err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 
++                                CTNL_MSG_NEWCONNTRACK, 1, ct, NULL, 
++                                proto, NULL, NULL);
++      ip_conntrack_put(ct);
++      if (err <= 0)
++              goto nlmsg_failure;
++
++      err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);
++      if (err < 0)
++              return err;
++      return 0;
++
++nlmsg_failure:
++      if (skb2)
++              kfree_skb(skb2);
++      return -1;
++}
++
++/* Finish me: should support NLM_F_CREATE and NLM_F_REPLACE. */
++static int 
++ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, 
++                      struct nlmsghdr *nlh, int *errp)
++{
++      return -EOPNOTSUPP;
++}
++
++
++/* EXPECT */
++
++static int
++ctnetlink_exp_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
++                  int event, 
++                  int nowait, 
++                  const struct ip_conntrack_expect *exp)
++{
++      struct nlmsghdr *nlh;
++      struct nfgenmsg *nfmsg;
++      unsigned char *b;
++
++      b = skb->tail;
++      nlh = NLMSG_PUT(skb, pid, seq, (NFNL_SUBSYS_CTNETLINK<<8)|event, 
++                      sizeof(struct nfgenmsg));
++      nlh->nlmsg_flags        = (nowait && pid) ? NLM_F_MULTI : 0;
++      nfmsg = NLMSG_DATA(nlh);
++      nfmsg->nfgen_family = AF_INET;
++
++      NFA_PUT(skb, CTA_EXP_TUPLE, sizeof(struct ip_conntrack_tuple),
++              &exp->tuple);
++      NFA_PUT(skb, CTA_EXP_MASK, sizeof(struct ip_conntrack_tuple),
++              &exp->mask);
++      NFA_PUT(skb, CTA_EXP_SEQNO, sizeof(u_int32_t), &exp->seq);
++      NFA_PUT(skb, CTA_EXP_HELP, sizeof(union ip_conntrack_expect_help),
++              &exp->help);
++
++      /* FIXME: proto */
++
++#ifdef CONFIG_IP_NF_NAT_NEEDED
++#endif /* CONFIG_IP_NF_NAT_NEEDED */
++
++      nlh->nlmsg_len = skb->tail - b;
++      return skb->len;
++
++nlmsg_failure:
++nfattr_failure:
++      skb_trim(skb, b - skb->data);
++      return -1;
++}
++
++static inline struct sk_buff *
++ctnetlink_exp_event_build_msg(const struct ip_conntrack_expect *exp)
++{
++      struct sk_buff *skb;
++      int err;
++
++      skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
++      if (!skb)
++              return NULL;
++
++      err = ctnetlink_exp_fill_info(skb, 0, 0, CTNL_MSG_NEWEXPECT, 1, exp);
++      if (err <= 0)
++              goto nlmsg_failure;
++      return skb;
++
++nlmsg_failure:
++      if (skb)
++              kfree_skb(skb);
++        return NULL;
++}
++
++static void
++ctnetlink_exp_create(struct ip_conntrack_expect *exp)
++{
++      u16 proto = exp->tuple.dst.protonum;
++      struct sk_buff *skb;
++
++      skb = ctnetlink_exp_event_build_msg(exp);
++      if (!skb)
++              return;
++
++      if (proto == IPPROTO_TCP) {
++              nfnetlink_send(skb, 0, NFGRP_IPV4_CT_TCP, 0);
++              return;
++              } else if (proto == IPPROTO_UDP) {
++              nfnetlink_send(skb, 0, NFGRP_IPV4_CT_UDP, 0);
++              return;
++              } else if (proto == IPPROTO_ICMP) {
++              nfnetlink_send(skb, 0, NFGRP_IPV4_CT_ICMP, 0);
++              return;
++      } else {
++              nfnetlink_send(skb, 0, NFGRP_IPV4_CT_OTHER, 0);
++              return;
++      }
++      kfree_skb(skb);
++      return;
++}
++
++
++static int
++ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, 
++                   struct nlmsghdr *nlh, int *errp)
++{
++        struct ip_conntrack_expect *exp;
++      struct ip_conntrack_tuple *tuple;
++      struct nfattr *cda[CTA_MAX];
++
++      if (nfnetlink_check_attributes(ctnl_subsys, nlh, cda) < 0)
++              return -EINVAL;
++
++      if (cda[CTA_ORIG-1])
++              tuple = NFA_DATA(cda[CTA_ORIG-1]);
++      else {
++              if (cda[CTA_RPLY-1])
++                      tuple = NFA_DATA(cda[CTA_RPLY-1]);
++              else
++                      return -EINVAL;
++      }
++
++      /* bump usage count to 2 */
++      exp = ip_conntrack_expect_find_get(tuple);
++      if (!exp)
++              return -ENOENT;
++
++      /* after list removal, usage count == 1 */
++      ip_conntrack_unexpect_related(exp);
++      /* we have put what we 'get' above. after this line usage count == 0 */
++      ip_conntrack_expect_put(exp);
++
++      return 0;
++}
++
++static int
++ctnetlink_exp_dump_build_msg(const struct ip_conntrack_expect *exp,
++                       struct sk_buff *skb, u32 pid, u32 seq)
++{
++      int err, proto;
++
++      proto = exp->tuple.dst.protonum;
++      err = ctnetlink_exp_fill_info(skb, pid, seq, CTNL_MSG_NEWEXPECT, 1, 
++                                    exp);
++      if (err <= 0)
++              goto nlmsg_failure;
++      return 0;
++
++nlmsg_failure:
++      if (skb)
++              kfree_skb(skb);
++      return -1;
++}
++
++static int
++ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
++{
++      ct_debug(0, "entered\n");
++      if (cb->args[0] == 0) {
++              READ_LOCK(&ip_conntrack_lock);
++              LIST_FIND(&ip_conntrack_expect_list, 
++                        ctnetlink_exp_dump_build_msg,
++                        struct ip_conntrack_expect *, skb,
++                        NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq);
++              READ_UNLOCK(&ip_conntrack_lock);
++              cb->args[0] = 1;
++      }
++      ct_debug(0, "returning\n");
++
++      return skb->len;
++}
++
++
++static int
++ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, 
++                   struct nlmsghdr *nlh, int *errp)
++{
++      struct ip_conntrack_expect *exp;
++      struct ip_conntrack_tuple *tuple;
++      struct nfattr *cda[CTA_MAX];
++      struct sk_buff *skb2 = NULL;
++      int err, proto;
++
++      ct_debug(0, "entered\n");
++
++      if (nlh->nlmsg_flags & NLM_F_DUMP) {
++              struct nfgenmsg *msg = NLMSG_DATA(nlh);
++              u32 rlen;
++
++              if (msg->nfgen_family != AF_INET)
++                      return -EAFNOSUPPORT;
++
++              ct_debug(0, "starting dump\n");
++                if ((*errp = netlink_dump_start(ctnl, skb, nlh,
++                                              ctnetlink_exp_dump_table,
++                                              ctnetlink_done)) != 0)
++                      return -EINVAL;
++              rlen = NLMSG_ALIGN(nlh->nlmsg_len);
++              if (rlen > skb->len)
++                      rlen = skb->len;
++              skb_pull(skb, rlen);
++              return 0;
++      }
++
++      if (nfnetlink_check_attributes(ctnl_subsys, nlh, cda) < 0)
++              return -EINVAL;
++
++      if (cda[CTA_ORIG-1])
++              tuple = NFA_DATA(cda[CTA_ORIG-1]);
++      else {
++              if (cda[CTA_RPLY-1])
++                      tuple = NFA_DATA(cda[CTA_RPLY-1]);
++              else
++                      return -EINVAL;
++      }
++
++      exp = ip_conntrack_expect_find_get(tuple);
++      if (!exp)
++              return -ENOENT;
++
++      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
++      if (!skb2)
++              return -ENOMEM;
++      NETLINK_CB(skb2).dst_pid = NETLINK_CB(skb).pid;
++      proto = exp->tuple.dst.protonum;
++      
++      err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid, 
++                                    nlh->nlmsg_seq, CTNL_MSG_NEWEXPECT,
++                                    1, exp);
++      if (err <= 0)
++              goto nlmsg_failure;
++
++      err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);
++      if (err < 0)
++              return err;
++      return 0;
++
++nlmsg_failure:
++      if (skb2)
++              kfree_skb(skb2);
++      return -1;
++}
++
++static int
++ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
++                   struct nlmsghdr *nlh, int *errp)
++{
++      return -EOPNOTSUPP;
++}
++
++/* struct conntrack_expect stuff */
++
++#if 0
++static struct ip_conntrack_notify ctnl_notify = { { NULL, NULL },
++                                              ctnetlink_destroy,
++                                              ctnetlink_create };
++
++static struct ip_conntrack_notify ctnl_exp_notify;
++#endif
++
++static void __exit ctnetlink_exit(void)
++{
++      printk("ctnetlink: unregistering with nfnetlink.\n");
++//    ip_conntrack_notify_unregister(&ctnl_exp_notify);
++//    ip_conntrack_notify_unregister(&ctnl_notify);
++      nfnetlink_subsys_unregister(ctnl_subsys);
++      kfree(ctnl_subsys);
++      return;
++}
++
++static int __init ctnetlink_init(void)
++{
++      int ret;
++
++      ctnl_subsys = nfnetlink_subsys_alloc(CTNL_MSG_COUNT);
++      if (!ctnl_subsys) {
++              ret = -ENOMEM;
++              goto err_out; 
++      }
++
++      ctnl_subsys->name = "conntrack";
++      ctnl_subsys->subsys_id = NFNL_SUBSYS_CTNETLINK;
++      ctnl_subsys->cb_count = CTNL_MSG_COUNT;
++      ctnl_subsys->attr_count = CTA_MAX;
++      ctnl_subsys->cb[CTNL_MSG_NEWCONNTRACK].call = ctnetlink_new_conntrack;
++      ctnl_subsys->cb[CTNL_MSG_NEWCONNTRACK].cap_required = CAP_NET_ADMIN;
++      ctnl_subsys->cb[CTNL_MSG_DELCONNTRACK].call = ctnetlink_del_conntrack;
++      ctnl_subsys->cb[CTNL_MSG_DELCONNTRACK].cap_required = CAP_NET_ADMIN;
++      ctnl_subsys->cb[CTNL_MSG_GETCONNTRACK].call = ctnetlink_get_conntrack;
++      ctnl_subsys->cb[CTNL_MSG_GETCONNTRACK].cap_required = 0;
++      ctnl_subsys->cb[CTNL_MSG_NEWEXPECT].call = ctnetlink_new_expect;
++      ctnl_subsys->cb[CTNL_MSG_NEWEXPECT].cap_required = CAP_NET_ADMIN;
++      ctnl_subsys->cb[CTNL_MSG_DELEXPECT].call = ctnetlink_del_expect;
++      ctnl_subsys->cb[CTNL_MSG_DELEXPECT].cap_required = CAP_NET_ADMIN;
++      ctnl_subsys->cb[CTNL_MSG_GETEXPECT].call = ctnetlink_get_expect;
++      ctnl_subsys->cb[CTNL_MSG_GETEXPECT].cap_required = 0;
++      // FIXME: CONFIRM
++
++      printk("ctnetlink: registering with nfnetlink v%s.\n", ctversion);
++      if (nfnetlink_subsys_register(ctnl_subsys) < 0) {
++              printk("ctnetlink_init: cannot register with nfnetlink.\n");
++              ret = -1;
++              goto err_free_subsys;
++      }
++
++
++#if 0
++      if (ip_conntrack_notify_register(&ctnl_notify) < 0) {
++              printk("ctnetlink_init: cannot register notifier.\n");
++              ret = -1;
++              goto err_unreg_subsys;
++      }
++
++      if (ip_conntrack_notify_register(&ctnl_exp_notify) < 0) {
++              printk("ctnetlink_init: cannot register exp notifier\n");
++              ret = -1;
++              goto err_unreg_notify;
++      }
++#endif
++
++
++      return 0;
++      
++#if 0
++err_unreg_notify:
++      ip_conntrack_notify_unregister(&ctnl_notify);
++#endif 
++err_unreg_subsys:
++      nfnetlink_subsys_unregister(ctnl_subsys);
++err_free_subsys:
++      kfree(ctnl_subsys);
++err_out:
++      return ret;
++}
++
++module_init(ctnetlink_init);
++module_exit(ctnetlink_exit);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/ip6_output.c linux-2.4.20/net/ipv6/ip6_output.c
+--- linux-2.4.20.org/net/ipv6/ip6_output.c     Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv6/ip6_output.c Wed Sep 24 09:16:14 2003
+@@ -132,7 +132,7 @@
+ #ifdef CONFIG_NETFILTER
+-static int route6_me_harder(struct sk_buff *skb)
++int ip6_route_me_harder(struct sk_buff *skb)
+ {
+       struct ipv6hdr *iph = skb->nh.ipv6h;
+       struct dst_entry *dst;
+@@ -150,7 +150,7 @@
+       if (dst->error) {
+               if (net_ratelimit())
+-                      printk(KERN_DEBUG "route6_me_harder: No more route.\n");
++                      printk(KERN_DEBUG "ip6_route_me_harder: No more route.\n");
+               return -EINVAL;
+       }
+@@ -166,7 +166,7 @@
+ {
+ #ifdef CONFIG_NETFILTER
+       if (skb->nfcache & NFC_ALTERED){
+-              if (route6_me_harder(skb) != 0){
++              if (ip6_route_me_harder(skb) != 0){
+                       kfree_skb(skb);
+                       return -EINVAL;
+               }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/Config.in linux-2.4.20/net/ipv6/netfilter/Config.in
+--- linux-2.4.20.org/net/ipv6/netfilter/Config.in      Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv6/netfilter/Config.in  Wed Sep 24 09:18:15 2003
+@@ -15,15 +15,40 @@
+ tristate 'IP6 tables support (required for filtering/masq/NAT)' CONFIG_IP6_NF_IPTABLES
+ if [ "$CONFIG_IP6_NF_IPTABLES" != "n" ]; then
++  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++    tristate 'raw table support (required for TRACE)' CONFIG_IP6_NF_RAW $CONFIG_IP6_NF_IPTABLES
++  fi
+ # The simple matches.
+   dep_tristate '  limit match support' CONFIG_IP6_NF_MATCH_LIMIT $CONFIG_IP6_NF_IPTABLES
+   dep_tristate '  MAC address match support' CONFIG_IP6_NF_MATCH_MAC $CONFIG_IP6_NF_IPTABLES
++  dep_tristate '  Condition variable match support' CONFIG_IP6_NF_MATCH_CONDITION $CONFIG_IP6_NF_IPTABLES
++  dep_tristate '  Random match support' CONFIG_IP6_NF_MATCH_RANDOM $CONFIG_IP6_NF_IPTABLES
++  dep_tristate '  Nth match support' CONFIG_IP6_NF_MATCH_NTH $CONFIG_IP6_NF_IPTABLES
++  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++    dep_tristate '    Fuzzy match support' CONFIG_IP6_NF_MATCH_FUZZY $CONFIG_IP6_NF_FILTER
++  fi
++  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++    dep_tristate '  Routing header match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_RT $CONFIG_IP6_NF_IPTABLES
++  fi
++  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++    dep_tristate '  Hop-by-Hop and Dst opts header match (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_OPTS $CONFIG_IP6_NF_IPTABLES
++  fi
++  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++    dep_tristate '  Fragmentation header match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_FRAG $CONFIG_IP6_NF_IPTABLES
++  fi
++  dep_tristate '  HL match support' CONFIG_IP6_NF_MATCH_HL $CONFIG_IP6_NF_IPTABLES
+   dep_tristate '  Multiple port match support' CONFIG_IP6_NF_MATCH_MULTIPORT $CONFIG_IP6_NF_IPTABLES
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+     dep_tristate '  Owner match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_OWNER $CONFIG_IP6_NF_IPTABLES
+   fi
+ #  dep_tristate '  MAC address match support' CONFIG_IP6_NF_MATCH_MAC $CONFIG_IP6_NF_IPTABLES
+   dep_tristate '  netfilter MARK match support' CONFIG_IP6_NF_MATCH_MARK $CONFIG_IP6_NF_IPTABLES
++  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++    dep_tristate '  IPv6 Extension Headers Match (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_IPV6HEADER $CONFIG_IP6_NF_IPTABLES
++  fi
++  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++    dep_tristate '  AH/ESP match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_AHESP $CONFIG_IP6_NF_IPTABLES
++  fi
+   dep_tristate '  Packet Length match support' CONFIG_IP6_NF_MATCH_LENGTH $CONFIG_IP6_NF_IPTABLES
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+     dep_tristate '  EUI64 address check (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_EUI64 $CONFIG_IP6_NF_IPTABLES
+@@ -39,8 +64,20 @@
+ #  fi
+ # The targets
++  if [ "$CONFIG_IP6_NF_RAW" != "n" ]; then
++    dep_tristate '    TRACE target support' CONFIG_IP6_NF_TARGET_TRACE $CONFIG_IP6_NF_RAW
++    if [ "$CONFIG_IP6_NF_TARGET_TRACE" != "n" ]; then
++      define_bool CONFIG_IP6_NF_TARGET_TRACE_NEEDED y
++    fi
++  fi
+   dep_tristate '  Packet filtering' CONFIG_IP6_NF_FILTER $CONFIG_IP6_NF_IPTABLES 
+   if [ "$CONFIG_IP6_NF_FILTER" != "n" ]; then
++    dep_tristate '    REJECT target support' CONFIG_IP6_NF_TARGET_REJECT $CONFIG_IP6_NF_FILTER
++  fi
++  if [ "$CONFIG_IP6_NF_FILTER" != "n" ]; then
++    dep_tristate '    HL target support' CONFIG_IP6_NF_TARGET_HL $CONFIG_IP6_NF_FILTER
++  fi
++  if [ "$CONFIG_IP6_NF_FILTER" != "n" ]; then
+     dep_tristate '    LOG target support' CONFIG_IP6_NF_TARGET_LOG $CONFIG_IP6_NF_FILTER
+   fi
+@@ -55,6 +92,8 @@
+   if [ "$CONFIG_IP6_NF_MANGLE" != "n" ]; then
+ #    dep_tristate '    TOS target support' CONFIG_IP6_NF_TARGET_TOS $CONFIG_IP_NF_MANGLE
+     dep_tristate '    MARK target support' CONFIG_IP6_NF_TARGET_MARK $CONFIG_IP6_NF_MANGLE
++    dep_mbool '    ROUTE target support' CONFIG_IP6_NF_TARGET_ROUTE $CONFIG_IP6_NF_MANGLE
++    dep_tristate '    IMQ target support' CONFIG_IP6_NF_TARGET_IMQ $CONFIG_IP6_NF_MANGLE
+   fi
+   #dep_tristate '  LOG target support' CONFIG_IP6_NF_TARGET_LOG $CONFIG_IP6_NF_IPTABLES
+ fi
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/Makefile linux-2.4.20/net/ipv6/netfilter/Makefile
+--- linux-2.4.20.org/net/ipv6/netfilter/Makefile       Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv6/netfilter/Makefile   Wed Sep 24 09:18:15 2003
+@@ -15,15 +15,33 @@
+ obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o
+ obj-$(CONFIG_IP6_NF_MATCH_LIMIT) += ip6t_limit.o
+ obj-$(CONFIG_IP6_NF_MATCH_MARK) += ip6t_mark.o
++obj-$(CONFIG_IP6_NF_MATCH_CONDITION) += ip6t_condition.o
+ obj-$(CONFIG_IP6_NF_MATCH_LENGTH) += ip6t_length.o
+ obj-$(CONFIG_IP6_NF_MATCH_MAC) += ip6t_mac.o
++obj-$(CONFIG_IP6_NF_MATCH_FUZZY) += ip6t_fuzzy.o
++obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o
++obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o ip6t_dst.o
++obj-$(CONFIG_IP6_NF_MATCH_IPV6HEADER) += ip6t_ipv6header.o
++obj-$(CONFIG_IP6_NF_MATCH_FRAG) += ip6t_frag.o
++obj-$(CONFIG_IP6_NF_MATCH_AHESP) += ip6t_esp.o ip6t_ah.o
+ obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o
+ obj-$(CONFIG_IP6_NF_MATCH_MULTIPORT) += ip6t_multiport.o
+ obj-$(CONFIG_IP6_NF_MATCH_OWNER) += ip6t_owner.o
+ obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o
+ obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o
+ obj-$(CONFIG_IP6_NF_TARGET_MARK) += ip6t_MARK.o
++obj-$(CONFIG_IP6_NF_TARGET_ROUTE) += ip6t_ROUTE.o
++obj-$(CONFIG_IP6_NF_TARGET_IMQ) += ip6t_IMQ.o
++obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o
+ obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o
+ obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o
++obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o
++obj-$(CONFIG_IP6_NF_TARGET_TRACE) += ip6t_TRACE.o
++
++obj-$(CONFIG_IP6_NF_MATCH_RANDOM) += ip6t_random.o
++
++obj-$(CONFIG_IP6_NF_MATCH_NTH) += ip6t_nth.o
++obj-$(CONFIG_IP6_NF_TARGET_HL) += ip6t_HL.o
++obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o
+ include $(TOPDIR)/Rules.make
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6_queue.c linux-2.4.20/net/ipv6/netfilter/ip6_queue.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6_queue.c    Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv6/netfilter/ip6_queue.c        Wed Sep 24 09:16:14 2003
+@@ -305,8 +305,9 @@
+       write_lock_bh(&queue_lock);
+       
+       if (!peer_pid)
+-              goto err_out_unlock;
++              goto err_out_free_nskb; 
++      /* netlink_unicast will either free the nskb or attach it to a socket */ 
+       status = netlink_unicast(ipqnl, nskb, peer_pid, MSG_DONTWAIT);
+       if (status < 0)
+               goto err_out_unlock;
+@@ -318,6 +319,9 @@
+       write_unlock_bh(&queue_lock);
+       return status;
+       
++err_out_free_nskb:
++      kfree_skb(nskb); 
++      
+ err_out_unlock:
+       write_unlock_bh(&queue_lock);
+@@ -326,45 +330,6 @@
+       return status;
+ }
+-/*
+- * Taken from net/ipv6/ip6_output.c
+- *
+- * We should use the one there, but is defined static
+- * so we put this just here and let the things as
+- * they are now.
+- *
+- * If that one is modified, this one should be modified too.
+- */
+-static int
+-route6_me_harder(struct sk_buff *skb)
+-{
+-      struct ipv6hdr *iph = skb->nh.ipv6h;
+-      struct dst_entry *dst;
+-      struct flowi fl;
+-
+-      fl.proto = iph->nexthdr;
+-      fl.fl6_dst = &iph->daddr;
+-      fl.fl6_src = &iph->saddr;
+-      fl.oif = skb->sk ? skb->sk->bound_dev_if : 0;
+-      fl.fl6_flowlabel = 0;
+-      fl.uli_u.ports.dport = 0;
+-      fl.uli_u.ports.sport = 0;
+-
+-      dst = ip6_route_output(skb->sk, &fl);
+-
+-      if (dst->error) {
+-              if (net_ratelimit())
+-                      printk(KERN_DEBUG "route6_me_harder: No more route.\n");
+-              return -EINVAL;
+-      }
+-
+-      /* Drop old route. */
+-      dst_release(skb->dst);
+-
+-      skb->dst = dst;
+-      return 0;
+-}
+-
+ static int
+ ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct ipq_queue_entry *e)
+ {
+@@ -410,7 +375,7 @@
+               struct ipv6hdr *iph = e->skb->nh.ipv6h;
+               if (ipv6_addr_cmp(&iph->daddr, &e->rt_info.daddr) ||
+                   ipv6_addr_cmp(&iph->saddr, &e->rt_info.saddr))
+-                      return route6_me_harder(e->skb);
++                      return ip6_route_me_harder(e->skb);
+       }
+       return 0;
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6_tables.c linux-2.4.20/net/ipv6/netfilter/ip6_tables.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6_tables.c   Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv6/netfilter/ip6_tables.c       Wed Sep 24 09:18:14 2003
+@@ -25,10 +25,21 @@
+ #include <linux/proc_fs.h>
+ #include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6_logging.h>
+ #define IPV6_HDR_LEN  (sizeof(struct ipv6hdr))
+ #define IPV6_OPTHDR_LEN       (sizeof(struct ipv6_opt_hdr))
++#ifdef CONFIG_IP6_NF_TARGET_TRACE_NEEDED
++static const char *hook6names[] = { 
++      [NF_IP6_PRE_ROUTING] "PREROUTING",
++      [NF_IP6_LOCAL_IN] "INPUT",
++      [NF_IP6_FORWARD] "FORWARD",
++      [NF_IP6_LOCAL_OUT] "OUTPUT",
++      [NF_IP6_POST_ROUTING] "POSTROUTING",
++};
++#endif
++
+ /*#define DEBUG_IP_FIREWALL*/
+ /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
+ /*#define DEBUG_IP_FIREWALL_USER*/
+@@ -101,10 +112,8 @@
+       unsigned int hook_entry[NF_IP6_NUMHOOKS];
+       unsigned int underflow[NF_IP6_NUMHOOKS];
+-      char padding[SMP_ALIGN((NF_IP6_NUMHOOKS*2+2)*sizeof(unsigned int))];
+-
+       /* ip6t_entry tables: one per CPU */
+-      char entries[0];
++      char entries[0] ____cacheline_aligned;
+ };
+ static LIST_HEAD(ip6t_target);
+@@ -322,6 +331,39 @@
+       return (struct ip6t_entry *)(base + offset);
+ }
++static inline int
++get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
++                    char **chainname, u_int16_t *rulenum)
++{
++      struct ip6t_entry_target *t;
++
++      (*rulenum)++;
++
++      if (s == e)
++              return 1;
++
++      t = ip6t_get_target(s);
++      if (strcmp(t->u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
++              *chainname = t->data;
++              (*rulenum) = 0;
++      }
++      
++      return 0;
++}
++
++/* All zeroes == unconditional rule. */
++static inline int
++unconditional(const struct ip6t_ip6 *ipv6)
++{
++      unsigned int i;
++
++      for (i = 0; i < sizeof(*ipv6); i++)
++              if (((char *)ipv6)[i])
++                      break;
++
++      return (i == sizeof(*ipv6));
++}
++
+ /* Returns one of the generic firewall policies, like NF_ACCEPT. */
+ unsigned int
+ ip6t_do_table(struct sk_buff **pskb,
+@@ -398,6 +440,27 @@
+                       t = ip6t_get_target(e);
+                       IP_NF_ASSERT(t->u.kernel.target);
++#ifdef CONFIG_IP6_NF_TARGET_TRACE_NEEDED
++                      /* The packet traced and the rule isn't an unconditional return/END. */
++                      if (((*pskb)->nfcache & NFC_TRACE)
++                          && !(e->target_offset == sizeof(struct ip6t_entry)
++                               && (strcmp(t->u.kernel.target->name,
++                                     IP6T_STANDARD_TARGET) == 0)
++                               && !t->u.kernel.target->target
++                               && ((struct ip6t_standard_target *)t)->verdict < 0
++                               && unconditional(&e->ipv6))) {
++                              char *chainname = (char *) hook6names[hook];
++                              u_int16_t rulenum = 0;
++                              
++                              IP6T_ENTRY_ITERATE(get_entry(table_base, table->private->hook_entry[hook]),
++                                                 table->private->size,
++                                                 get_chainname_rulenum,
++                                                 e, &chainname, &rulenum);
++                                                
++                              nf_log_ip6_packet(pskb, hook, in, out, "TRACE: %s/%s/%u ",
++                                                table->name, chainname, rulenum);
++                      }
++#endif
+                       /* Standard target? */
+                       if (!t->u.kernel.target->target) {
+                               int v;
+@@ -554,19 +617,6 @@
+       return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
+ }
+-/* All zeroes == unconditional rule. */
+-static inline int
+-unconditional(const struct ip6t_ip6 *ipv6)
+-{
+-      unsigned int i;
+-
+-      for (i = 0; i < sizeof(*ipv6); i++)
+-              if (((char *)ipv6)[i])
+-                      break;
+-
+-      return (i == sizeof(*ipv6));
+-}
+-
+ /* Figures out from what hook each rule can be called: returns 0 if
+    there are loops.  Puts hook bitmask in comefrom. */
+ static int
+@@ -1450,7 +1500,7 @@
+       int ret;
+       struct ip6t_table_info *newinfo;
+       static struct ip6t_table_info bootstrap
+-              = { 0, 0, 0, { 0 }, { 0 }, { }, { } };
++              = { 0, 0, 0, { 0 }, { 0 }, { } };
+       MOD_INC_USE_COUNT;
+       newinfo = vmalloc(sizeof(struct ip6t_table_info)
+@@ -1767,14 +1817,15 @@
+ = { { NULL, NULL }, "icmp6", &icmp6_match, &icmp6_checkentry, NULL };
+ #ifdef CONFIG_PROC_FS
+-static inline int print_name(const struct ip6t_table *t,
++static inline int print_name(const char *i,
+                            off_t start_offset, char *buffer, int length,
+                            off_t *pos, unsigned int *count)
+ {
+       if ((*count)++ >= start_offset) {
+               unsigned int namelen;
+-              namelen = sprintf(buffer + *pos, "%s\n", t->name);
++              namelen = sprintf(buffer + *pos, "%s\n",
++                                i + sizeof(struct list_head));
+               if (*pos + namelen > length) {
+                       /* Stop iterating */
+                       return 1;
+@@ -1792,7 +1843,7 @@
+       if (down_interruptible(&ip6t_mutex) != 0)
+               return 0;
+-      LIST_FIND(&ip6t_tables, print_name, struct ip6t_table *,
++      LIST_FIND(&ip6t_tables, print_name, char *,
+                 offset, buffer, length, &pos, &count);
+       up(&ip6t_mutex);
+@@ -1801,6 +1852,46 @@
+       *start=(char *)((unsigned long)count-offset);
+       return pos;
+ }
++
++static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
++{
++      off_t pos = 0;
++      unsigned int count = 0;
++
++      if (down_interruptible(&ip6t_mutex) != 0)
++              return 0;
++
++      LIST_FIND(&ip6t_target, print_name, char *,
++                offset, buffer, length, &pos, &count);
++
++      up(&ip6t_mutex);
++
++      *start = (char *)((unsigned long)count - offset);
++      return pos;
++}
++
++static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
++{
++      off_t pos = 0;
++      unsigned int count = 0;
++
++      if (down_interruptible(&ip6t_mutex) != 0)
++              return 0;
++
++      LIST_FIND(&ip6t_match, print_name, char *,
++                offset, buffer, length, &pos, &count);
++
++      up(&ip6t_mutex);
++
++      *start = (char *)((unsigned long)count - offset);
++      return pos;
++}
++
++static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
++{ { "ip6_tables_names", ip6t_get_tables },
++  { "ip6_tables_targets", ip6t_get_targets },
++  { "ip6_tables_matches", ip6t_get_matches },
++  { NULL, NULL} };
+ #endif /*CONFIG_PROC_FS*/
+ static int __init init(void)
+@@ -1826,13 +1917,19 @@
+ #ifdef CONFIG_PROC_FS
+       {
+               struct proc_dir_entry *proc;
+-              proc = proc_net_create("ip6_tables_names", 0,
+-                                      ip6t_get_tables);
+-              if (!proc) {
+-                      nf_unregister_sockopt(&ip6t_sockopts);
+-                      return -ENOMEM;
++              int i;
++
++              for (i = 0; ip6t_proc_entry[i].name; i++) {
++                      proc = proc_net_create(ip6t_proc_entry[i].name, 0,
++                                             ip6t_proc_entry[i].get_info);
++                      if (!proc) {
++                              while (--i >= 0)
++                                     proc_net_remove(ip6t_proc_entry[i].name);
++                              nf_unregister_sockopt(&ip6t_sockopts);
++                              return -ENOMEM;
++                      }
++                      proc->owner = THIS_MODULE;
+               }
+-              proc->owner = THIS_MODULE;
+       }
+ #endif
+@@ -1844,7 +1941,11 @@
+ {
+       nf_unregister_sockopt(&ip6t_sockopts);
+ #ifdef CONFIG_PROC_FS
+-      proc_net_remove("ip6_tables_names");
++      {
++              int i;
++              for (i = 0; ip6t_proc_entry[i].name; i++)
++                      proc_net_remove(ip6t_proc_entry[i].name);
++      }
+ #endif
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_HL.c linux-2.4.20/net/ipv6/netfilter/ip6t_HL.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_HL.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_HL.c  Wed Sep 24 09:16:28 2003
+@@ -0,0 +1,105 @@
++/* 
++ * Hop Limit modification target for ip6tables
++ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
++ * Based on HW's TTL module
++ *
++ * This software is distributed under the terms of GNU GPL
++ */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_HL.h>
++
++MODULE_AUTHOR("Maciej Soltysiak <solt@dns.toxicfilms.tv>");
++MODULE_DESCRIPTION("IP tables Hop Limit modification module");
++MODULE_LICENSE("GPL");
++
++static unsigned int ip6t_hl_target(struct sk_buff **pskb, unsigned int hooknum,
++              const struct net_device *in, const struct net_device *out,
++              const void *targinfo, void *userinfo)
++{
++      struct ipv6hdr *ip6h = (*pskb)->nh.ipv6h;
++      const struct ip6t_HL_info *info = targinfo;
++      u_int16_t diffs[2];
++      int new_hl;
++                       
++      switch (info->mode) {
++              case IP6T_HL_SET:
++                      new_hl = info->hop_limit;
++                      break;
++              case IP6T_HL_INC:
++                      new_hl = ip6h->hop_limit + info->hop_limit;
++                      if (new_hl > 255)
++                              new_hl = 255;
++                      break;
++              case IP6T_HL_DEC:
++                      new_hl = ip6h->hop_limit + info->hop_limit;
++                      if (new_hl < 0)
++                              new_hl = 0;
++                      break;
++              default:
++                      new_hl = ip6h->hop_limit;
++                      break;
++      }
++
++      if (new_hl != ip6h->hop_limit) {
++              diffs[0] = htons(((unsigned)ip6h->hop_limit) << 8) ^ 0xFFFF;
++              ip6h->hop_limit = new_hl;
++              diffs[1] = htons(((unsigned)ip6h->hop_limit) << 8);
++      }
++
++      return IP6T_CONTINUE;
++}
++
++static int ip6t_hl_checkentry(const char *tablename,
++              const struct ip6t_entry *e,
++              void *targinfo,
++              unsigned int targinfosize,
++              unsigned int hook_mask)
++{
++      struct ip6t_HL_info *info = targinfo;
++
++      if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_HL_info))) {
++              printk(KERN_WARNING "HL: targinfosize %u != %Zu\n",
++                              targinfosize,
++                              IP6T_ALIGN(sizeof(struct ip6t_HL_info)));
++              return 0;       
++      }       
++
++      if (strcmp(tablename, "mangle")) {
++              printk(KERN_WARNING "HL: can only be called from \"mangle\" table, not \"%s\"\n", tablename);
++              return 0;
++      }
++
++      if (info->mode > IP6T_HL_MAXMODE) {
++              printk(KERN_WARNING "HL: invalid or unknown Mode %u\n", 
++                      info->mode);
++              return 0;
++      }
++
++      if ((info->mode != IP6T_HL_SET) && (info->hop_limit == 0)) {
++              printk(KERN_WARNING "HL: increment/decrement doesn't make sense with value 0\n");
++              return 0;
++      }
++      
++      return 1;
++}
++
++static struct ip6t_target ip6t_HL = { { NULL, NULL }, "HL", 
++      ip6t_hl_target, ip6t_hl_checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      return ip6t_register_target(&ip6t_HL);
++}
++
++static void __exit fini(void)
++{
++      ip6t_unregister_target(&ip6t_HL);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_IMQ.c linux-2.4.20/net/ipv6/netfilter/ip6t_IMQ.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_IMQ.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_IMQ.c Wed Sep 24 09:17:21 2003
+@@ -0,0 +1,78 @@
++/* This target marks packets to be enqueued to an imq device */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_IMQ.h>
++#include <linux/imq.h>
++
++static unsigned int imq_target(struct sk_buff **pskb,
++                             unsigned int hooknum,
++                             const struct net_device *in,
++                             const struct net_device *out,
++                             const void *targinfo,
++                             void *userinfo)
++{
++      struct ip6t_imq_info *mr = (struct ip6t_imq_info*)targinfo;
++
++      (*pskb)->imq_flags = mr->todev | IMQ_F_ENQUEUE;
++      (*pskb)->nfcache |= NFC_ALTERED;
++
++      return IP6T_CONTINUE;
++}
++
++static int imq_checkentry(const char *tablename,
++                        const struct ip6t_entry *e,
++                        void *targinfo,
++                        unsigned int targinfosize,
++                        unsigned int hook_mask)
++{
++      struct ip6t_imq_info *mr;
++
++      if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_imq_info))) {
++              printk(KERN_WARNING "IMQ: invalid targinfosize\n");
++              return 0;
++      }
++      mr = (struct ip6t_imq_info*)targinfo;
++
++      if (strcmp(tablename, "mangle") != 0) {
++              printk(KERN_WARNING
++                     "IMQ: IMQ can only be called from \"mangle\" table, not \"%s\"\n",
++                     tablename);
++              return 0;
++      }
++      
++      if (mr->todev > IMQ_MAX_DEVS) {
++              printk(KERN_WARNING
++                     "IMQ: invalid device specified, highest is %u\n",
++                     IMQ_MAX_DEVS);
++              return 0;
++      }
++      
++      return 1;
++}
++
++static struct ip6t_target ip6t_imq_reg = {
++      { NULL, NULL},
++      "IMQ",
++      imq_target,
++      imq_checkentry,
++      NULL,
++      THIS_MODULE
++};
++
++static int __init init(void)
++{
++      if (ip6t_register_target(&ip6t_imq_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ip6t_unregister_target(&ip6t_imq_reg);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_LOG.c linux-2.4.20/net/ipv6/netfilter/ip6t_LOG.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_LOG.c     Thu Nov 28 23:53:15 2002
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_LOG.c Wed Sep 24 09:16:23 2003
+@@ -10,6 +10,7 @@
+ #include <net/tcp.h>
+ #include <net/ipv6.h>
+ #include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6_logging.h>
+ MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>");
+ MODULE_DESCRIPTION("IP6 tables LOG target module");
+@@ -89,7 +90,7 @@
+       printk("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr));
+       /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */
+-      printk("LEN=%u TC=%u HOPLIMIT=%u FLOWLBL=%u ",
++      printk("LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ",
+              ntohs(ipv6h->payload_len) + sizeof(struct ipv6hdr),
+              (ntohl(*(u_int32_t *)ipv6h) & 0x0ff00000) >> 20,
+              ipv6h->hop_limit,
+@@ -266,23 +267,21 @@
+       }
+ }
+-static unsigned int
+-ip6t_log_target(struct sk_buff **pskb,
++static void
++ip6t_log_packet(struct sk_buff **pskb,
+               unsigned int hooknum,
+               const struct net_device *in,
+               const struct net_device *out,
+-              const void *targinfo,
+-              void *userinfo)
++              const struct ip6t_log_info *loginfo,
++              const char *level_string,
++              const char *prefix)
+ {
+       struct ipv6hdr *ipv6h = (*pskb)->nh.ipv6h;
+-      const struct ip6t_log_info *loginfo = targinfo;
+-      char level_string[4] = "< >";
+-      level_string[1] = '0' + (loginfo->level % 8);
+       spin_lock_bh(&log_lock);
+       printk(level_string);
+       printk("%sIN=%s OUT=%s ",
+-              loginfo->prefix,
++              prefix == NULL ? loginfo->prefix : prefix,
+               in ? in->name : "",
+               out ? out->name : "");
+       if (in && !out) {
+@@ -329,10 +328,59 @@
+       dump_packet(loginfo, ipv6h, 1);
+       printk("\n");
+       spin_unlock_bh(&log_lock);
++}
++
++static unsigned int
++ip6t_log_target(struct sk_buff **pskb,
++              unsigned int hooknum,
++              const struct net_device *in,
++              const struct net_device *out,
++              const void *targinfo,
++              void *userinfo)
++{
++      const struct ip6t_log_info *loginfo = targinfo;
++      char level_string[4] = "< >";
++
++      level_string[1] = '0' + (loginfo->level % 8);
++      ip6t_log_packet(pskb, hooknum, in, out, loginfo, level_string, NULL);
+       return IP6T_CONTINUE;
+ }
++static void
++ip6_log_packet_fn(struct sk_buff **pskb,
++                unsigned int hooknum,
++                const struct net_device *in,
++                const struct net_device *out,
++                const char *prefix)
++{
++      struct ip6t_log_info loginfo = { 
++              .level = 0,
++              .logflags = IP6T_LOG_MASK,
++              .prefix = ""
++      };
++
++      ip6t_log_packet(pskb, hooknum, in, out, &loginfo, KERN_WARNING, prefix);
++}
++
++static void
++ip6_log_fn(char *pfh, size_t len,
++         const char *prefix)
++{
++      struct ipv6hdr *ipv6h = (struct ipv6hdr *)pfh;
++      struct ip6t_log_info loginfo = { 
++              .level = 0,
++              .logflags = IP6T_LOG_MASK,
++              .prefix = ""
++      };
++
++      spin_lock_bh(&log_lock);
++      printk(KERN_WARNING "%s", prefix);
++      dump_packet(&loginfo, ipv6h, 1);
++      printk("\n");
++      spin_unlock_bh(&log_lock);
++}
++
+ static int ip6t_log_checkentry(const char *tablename,
+                              const struct ip6t_entry *e,
+                              void *targinfo,
+@@ -364,17 +412,21 @@
+ static struct ip6t_target ip6t_log_reg
+ = { { NULL, NULL }, "LOG", ip6t_log_target, ip6t_log_checkentry, NULL, 
+     THIS_MODULE };
++static struct nf_logging_t ip6_logging_fn
++= { ip6_log_packet_fn, ip6_log_fn };
+ static int __init init(void)
+ {
+       if (ip6t_register_target(&ip6t_log_reg))
+               return -EINVAL;
++      nf_ip6_log_register(&ip6_logging_fn);
+       return 0;
+ }
+ static void __exit fini(void)
+ {
++      nf_ip6_log_unregister(&ip6_logging_fn);
+       ip6t_unregister_target(&ip6t_log_reg);
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_REJECT.c linux-2.4.20/net/ipv6/netfilter/ip6t_REJECT.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_REJECT.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_REJECT.c      Wed Sep 24 09:16:36 2003
+@@ -0,0 +1,274 @@
++/*
++ * This is a module which is used for rejecting packets.
++ *    Added support for customized reject packets (Jozsef Kadlecsik).
++ * Sun 12 Nov 2000
++ *    Port to IPv6 / ip6tables (Harald Welte <laforge@gnumonks.org>)
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/icmpv6.h>
++#include <net/tcp.h>
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_REJECT.h>
++
++#if 1
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++#if 0
++/* Send RST reply */
++static void send_reset(struct sk_buff *oldskb)
++{
++      struct sk_buff *nskb;
++      struct tcphdr *otcph, *tcph;
++      struct rtable *rt;
++      unsigned int otcplen;
++      int needs_ack;
++
++      /* IP header checks: fragment, too short. */
++      if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)
++          || oldskb->len < (oldskb->nh.iph->ihl<<2) + sizeof(struct tcphdr))
++              return;
++
++      otcph = (struct tcphdr *)((u_int32_t*)oldskb->nh.iph + oldskb->nh.iph->ihl);
++      otcplen = oldskb->len - oldskb->nh.iph->ihl*4;
++
++      /* No RST for RST. */
++      if (otcph->rst)
++              return;
++
++      /* Check checksum. */
++      if (tcp_v4_check(otcph, otcplen, oldskb->nh.iph->saddr,
++                       oldskb->nh.iph->daddr,
++                       csum_partial((char *)otcph, otcplen, 0)) != 0)
++              return;
++
++      /* Copy skb (even if skb is about to be dropped, we can't just
++           clone it because there may be other things, such as tcpdump,
++           interested in it) */
++      nskb = skb_copy(oldskb, GFP_ATOMIC);
++      if (!nskb)
++              return;
++
++      /* This packet will not be the same as the other: clear nf fields */
++      nf_conntrack_put(nskb->nfct);
++      nskb->nfct = NULL;
++      nskb->nfcache = 0;
++#ifdef CONFIG_NETFILTER_DEBUG
++      nskb->nf_debug = 0;
++#endif
++
++      tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
++
++      nskb->nh.iph->daddr = xchg(&nskb->nh.iph->saddr, nskb->nh.iph->daddr);
++      tcph->source = xchg(&tcph->dest, tcph->source);
++
++      /* Truncate to length (no data) */
++      tcph->doff = sizeof(struct tcphdr)/4;
++      skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));
++      nskb->nh.iph->tot_len = htons(nskb->len);
++
++      if (tcph->ack) {
++              needs_ack = 0;
++              tcph->seq = otcph->ack_seq;
++              tcph->ack_seq = 0;
++      } else {
++              needs_ack = 1;
++              tcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn + otcph->fin
++                                    + otcplen - (otcph->doff<<2));
++              tcph->seq = 0;
++      }
++
++      /* Reset flags */
++      ((u_int8_t *)tcph)[13] = 0;
++      tcph->rst = 1;
++      tcph->ack = needs_ack;
++
++      tcph->window = 0;
++      tcph->urg_ptr = 0;
++
++      /* Adjust TCP checksum */
++      tcph->check = 0;
++      tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),
++                                 nskb->nh.iph->saddr,
++                                 nskb->nh.iph->daddr,
++                                 csum_partial((char *)tcph,
++                                              sizeof(struct tcphdr), 0));
++
++      /* Adjust IP TTL, DF */
++      nskb->nh.iph->ttl = MAXTTL;
++      /* Set DF, id = 0 */
++      nskb->nh.iph->frag_off = htons(IP_DF);
++      nskb->nh.iph->id = 0;
++
++      /* Adjust IP checksum */
++      nskb->nh.iph->check = 0;
++      nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, 
++                                         nskb->nh.iph->ihl);
++
++      /* Routing */
++      if (ip_route_output(&rt, nskb->nh.iph->daddr, nskb->nh.iph->saddr,
++                          RT_TOS(nskb->nh.iph->tos) | RTO_CONN,
++                          0) != 0)
++              goto free_nskb;
++
++      dst_release(nskb->dst);
++      nskb->dst = &rt->u.dst;
++
++      /* "Never happens" */
++      if (nskb->len > nskb->dst->pmtu)
++              goto free_nskb;
++
++      NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
++              ip_finish_output);
++      return;
++
++ free_nskb:
++      kfree_skb(nskb);
++}
++#endif
++
++static unsigned int reject6_target(struct sk_buff **pskb,
++                         unsigned int hooknum,
++                         const struct net_device *in,
++                         const struct net_device *out,
++                         const void *targinfo,
++                         void *userinfo)
++{
++      const struct ip6t_reject_info *reject = targinfo;
++
++      /* WARNING: This code causes reentry within ip6tables.
++         This means that the ip6tables jump stack is now crap.  We
++         must return an absolute verdict. --RR */
++      DEBUGP("REJECTv6: calling icmpv6_send\n");
++      switch (reject->with) {
++      case IP6T_ICMP6_NO_ROUTE:
++              icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_NOROUTE, 0, out);
++              break;
++      case IP6T_ICMP6_ADM_PROHIBITED:
++              icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_ADM_PROHIBITED, 0, out);
++              break;
++      case IP6T_ICMP6_NOT_NEIGHBOUR:
++              icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_NOT_NEIGHBOUR, 0, out);
++              break;
++      case IP6T_ICMP6_ADDR_UNREACH:
++              icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, out);
++              break;
++      case IP6T_ICMP6_PORT_UNREACH:
++              icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, out);
++              break;
++#if 0
++      case IPT_ICMP_ECHOREPLY: {
++              struct icmp6hdr *icmph  = (struct icmphdr *)
++                      ((u_int32_t *)(*pskb)->nh.iph + (*pskb)->nh.iph->ihl);
++              unsigned int datalen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4;
++
++              /* Not non-head frags, or truncated */
++              if (((ntohs((*pskb)->nh.iph->frag_off) & IP_OFFSET) == 0)
++                  && datalen >= 4) {
++                      /* Usually I don't like cut & pasting code,
++                           but dammit, my party is starting in 45
++                           mins! --RR */
++                      struct icmp_bxm icmp_param;
++
++                      icmp_param.icmph=*icmph;
++                      icmp_param.icmph.type=ICMP_ECHOREPLY;
++                      icmp_param.data_ptr=(icmph+1);
++                      icmp_param.data_len=datalen;
++                      icmp_reply(&icmp_param, *pskb);
++              }
++      }
++      break;
++      case IPT_TCP_RESET:
++              send_reset(*pskb);
++              break;
++#endif
++      default:
++              printk(KERN_WARNING "REJECTv6: case %u not handled yet\n", reject->with);
++              break;
++      }
++
++      return NF_DROP;
++}
++
++static inline int find_ping_match(const struct ip6t_entry_match *m)
++{
++      const struct ip6t_icmp *icmpinfo = (const struct ip6t_icmp *)m->data;
++
++      if (strcmp(m->u.kernel.match->name, "icmp6") == 0
++          && icmpinfo->type == ICMPV6_ECHO_REQUEST
++          && !(icmpinfo->invflags & IP6T_ICMP_INV))
++              return 1;
++
++      return 0;
++}
++
++static int check(const char *tablename,
++               const struct ip6t_entry *e,
++               void *targinfo,
++               unsigned int targinfosize,
++               unsigned int hook_mask)
++{
++      const struct ip6t_reject_info *rejinfo = targinfo;
++
++      if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_reject_info))) {
++              DEBUGP("REJECTv6: targinfosize %u != 0\n", targinfosize);
++              return 0;
++      }
++
++      /* Only allow these for packet filtering. */
++      if (strcmp(tablename, "filter") != 0) {
++              DEBUGP("REJECTv6: bad table `%s'.\n", tablename);
++              return 0;
++      }
++      if ((hook_mask & ~((1 << NF_IP6_LOCAL_IN)
++                         | (1 << NF_IP6_FORWARD)
++                         | (1 << NF_IP6_LOCAL_OUT))) != 0) {
++              DEBUGP("REJECTv6: bad hook mask %X\n", hook_mask);
++              return 0;
++      }
++
++      if (rejinfo->with == IP6T_ICMP6_ECHOREPLY) {
++              /* Must specify that it's an ICMP ping packet. */
++              if (e->ipv6.proto != IPPROTO_ICMPV6
++                  || (e->ipv6.invflags & IP6T_INV_PROTO)) {
++                      DEBUGP("REJECTv6: ECHOREPLY illegal for non-icmp\n");
++                      return 0;
++              }
++              /* Must contain ICMP match. */
++              if (IP6T_MATCH_ITERATE(e, find_ping_match) == 0) {
++                      DEBUGP("REJECTv6: ECHOREPLY illegal for non-ping\n");
++                      return 0;
++              }
++      } else if (rejinfo->with == IP6T_TCP_RESET) {
++              /* Must specify that it's a TCP packet */
++              if (e->ipv6.proto != IPPROTO_TCP
++                  || (e->ipv6.invflags & IP6T_INV_PROTO)) {
++                      DEBUGP("REJECTv6: TCP_RESET illegal for non-tcp\n");
++                      return 0;
++              }
++      }
++
++      return 1;
++}
++
++static struct ip6t_target ip6t_reject_reg
++= { { NULL, NULL }, "REJECT", reject6_target, check, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ip6t_register_target(&ip6t_reject_reg))
++              return -EINVAL;
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ip6t_unregister_target(&ip6t_reject_reg);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_ROUTE.c linux-2.4.20/net/ipv6/netfilter/ip6t_ROUTE.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_ROUTE.c   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_ROUTE.c       Wed Sep 24 09:17:27 2003
+@@ -0,0 +1,289 @@
++/*
++ * This implements the ROUTE v6 target, which enables you to setup unusual
++ * routes not supported by the standard kernel routing table.
++ *
++ * Copyright (C) 2003 Cedric de Launois <delaunois@info.ucl.ac.be>
++ *
++ * v 1.0 2003/08/05
++ *
++ * This software is distributed under GNU GPL v2, 1991
++ */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ipv6.h>
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_ROUTE.h>
++#include <linux/netdevice.h>
++#include <net/ipv6.h>
++#include <net/ndisc.h>
++#include <net/ip6_route.h>
++#include <linux/icmpv6.h>
++
++#if 1
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++#define NIP6(addr) \
++      ntohs((addr).s6_addr16[0]), \
++      ntohs((addr).s6_addr16[1]), \
++      ntohs((addr).s6_addr16[2]), \
++      ntohs((addr).s6_addr16[3]), \
++      ntohs((addr).s6_addr16[4]), \
++      ntohs((addr).s6_addr16[5]), \
++      ntohs((addr).s6_addr16[6]), \
++      ntohs((addr).s6_addr16[7])
++
++/* Route the packet according to the routing keys specified in
++ * route_info. Keys are :
++ *  - ifindex : 
++ *      0 if no oif preferred, 
++ *      otherwise set to the index of the desired oif
++ *  - route_info->gw :
++ *      0 if no gateway specified,
++ *      otherwise set to the next host to which the pkt must be routed
++ * If success, skb->dev is the output device to which the packet must 
++ * be sent and skb->dst is not NULL
++ *
++ * RETURN:  1 if the packet was succesfully routed to the 
++ *            destination desired
++ *          0 if the kernel routing table could not route the packet
++ *            according to the keys specified
++ */
++static int 
++route6(struct sk_buff *skb,
++       unsigned int ifindex,
++       const struct ip6t_route_target_info *route_info)
++{
++      struct rt6_info *rt = NULL;
++      struct ipv6hdr *ipv6h = skb->nh.ipv6h;
++      struct in6_addr *gw = (struct in6_addr*)&route_info->gw;
++
++      DEBUGP("ip6t_ROUTE: called with: ");
++      DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr));
++      DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(*gw));
++      DEBUGP("OUT=%s\n", route_info->oif);
++      
++      if (ipv6_addr_any(gw))
++              rt = rt6_lookup(&ipv6h->daddr, &ipv6h->saddr, ifindex, 1);
++      else
++              rt = rt6_lookup(gw, &ipv6h->saddr, ifindex, 1);
++
++      if (!rt)
++              goto no_route;
++
++      DEBUGP("ip6t_ROUTE: routing gives: ");
++      DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_dst.addr));
++      DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_gateway));
++      DEBUGP("OUT=%s\n", rt->rt6i_dev->name);
++
++      if (ifindex && rt->rt6i_dev->ifindex!=ifindex)
++              goto wrong_route;
++      
++      if (!rt->rt6i_nexthop) {
++              DEBUGP("ip6t_ROUTE: discovering neighbour\n");
++              rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_dst.addr);
++      }
++
++      /* Drop old route. */
++      dst_release(skb->dst);
++      skb->dst = &rt->u.dst;
++      skb->dev = rt->rt6i_dev;
++      return 1;
++
++ wrong_route:
++      dst_release(&rt->u.dst);
++ no_route:
++      if (!net_ratelimit())
++              return 0;
++
++      printk("ip6t_ROUTE: no explicit route found ");
++      if (ifindex)
++              printk("via interface %s ", route_info->oif);
++      if (!ipv6_addr_any(gw))
++              printk("via gateway %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", NIP6(*gw));
++      printk("\n");
++      return 0;
++}
++
++
++/* Stolen from ip6_output_finish
++ * PRE : skb->dev is set to the device we are leaving by
++ *       skb->dst is not NULL
++ * POST: the packet is sent with the link layer header pushed
++ *       the packet is destroyed
++ */
++static void ip_direct_send(struct sk_buff *skb)
++{
++      struct dst_entry *dst = skb->dst;
++      struct hh_cache *hh = dst->hh;
++
++      if (hh) {
++              read_lock_bh(&hh->hh_lock);
++              memcpy(skb->data - 16, hh->hh_data, 16);
++              read_unlock_bh(&hh->hh_lock);
++              skb_push(skb, hh->hh_len);
++              hh->hh_output(skb);
++      } else if (dst->neighbour)
++              dst->neighbour->output(skb);
++      else {
++              if (net_ratelimit())
++                      DEBUGP(KERN_DEBUG "ip6t_ROUTE: no hdr & no neighbour cache!\n");
++              kfree_skb(skb);
++      }
++}
++
++
++static unsigned int 
++route6_oif(const struct ip6t_route_target_info *route_info,
++         struct sk_buff *skb) 
++{
++      unsigned int ifindex = 0;
++      struct net_device *dev_out = NULL;
++
++      /* The user set the interface name to use.
++       * Getting the current interface index.
++       */
++      if ((dev_out = dev_get_by_name(route_info->oif))) {
++              ifindex = dev_out->ifindex;
++      } else {
++              /* Unknown interface name : packet dropped */
++              if (net_ratelimit()) 
++                      DEBUGP("ip6t_ROUTE: oif interface %s not found\n", route_info->oif);
++
++              if (route_info->flags & IP6T_ROUTE_CONTINUE)
++                      return IP6T_CONTINUE;
++              else
++                      return NF_DROP;
++      }
++
++      /* Trying the standard way of routing packets */
++      if (route6(skb, ifindex, route_info)) {
++              dev_put(dev_out);
++              if (route_info->flags & IP6T_ROUTE_CONTINUE)
++                      return IP6T_CONTINUE;
++              
++              ip_direct_send(skb);
++              return NF_STOLEN;
++      } else 
++              return NF_DROP;
++}
++
++
++static unsigned int 
++route6_gw(const struct ip6t_route_target_info *route_info,
++        struct sk_buff *skb) 
++{
++      if (route6(skb, 0, route_info)) {
++              if (route_info->flags & IP6T_ROUTE_CONTINUE)
++                      return IP6T_CONTINUE;
++
++              ip_direct_send(skb);
++              return NF_STOLEN;
++      } else
++              return NF_DROP;
++}
++
++
++static unsigned int 
++ip6t_route_target(struct sk_buff **pskb,
++                unsigned int hooknum,
++                const struct net_device *in,
++                const struct net_device *out,
++                const void *targinfo,
++                void *userinfo)
++{
++      const struct ip6t_route_target_info *route_info = targinfo;
++      struct sk_buff *skb = *pskb;
++      struct in6_addr *gw = (struct in6_addr*)&route_info->gw;
++
++      if (route_info->flags & IP6T_ROUTE_CONTINUE)
++              goto do_it;
++
++      /* If we are at PREROUTING or INPUT hook
++       * the TTL isn't decreased by the IP stack
++       */
++      if (hooknum == NF_IP6_PRE_ROUTING ||
++          hooknum == NF_IP6_LOCAL_IN) {
++
++              struct ipv6hdr *ipv6h = skb->nh.ipv6h;
++
++              if (ipv6h->hop_limit <= 1) {
++                      /* Force OUTPUT device used as source address */
++                      skb->dev = skb->dst->dev;
++
++                      icmpv6_send(skb, ICMPV6_TIME_EXCEED, 
++                                  ICMPV6_EXC_HOPLIMIT, 0, skb->dev);
++
++                      return NF_DROP;
++              }
++
++              ipv6h->hop_limit--;
++      }
++
++
++ do_it:
++      if (route_info->oif[0]) 
++              return route6_oif(route_info, *pskb);
++      
++      if (!ipv6_addr_any(gw))
++              return route6_gw(route_info, *pskb);
++
++      if (net_ratelimit()) 
++              DEBUGP(KERN_DEBUG "ip6t_ROUTE: no parameter !\n");
++
++      return IP6T_CONTINUE;
++}
++
++
++static int 
++ip6t_route_checkentry(const char *tablename,
++                    const struct ip6t_entry *e,
++                    void *targinfo,
++                    unsigned int targinfosize,
++                    unsigned int hook_mask)
++{
++      if (strcmp(tablename, "mangle") != 0) {
++              printk("ip6t_ROUTE: can only be called from \"mangle\" table.\n");
++              return 0;
++      }
++
++      if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_route_target_info))) {
++              printk(KERN_WARNING "ip6t_ROUTE: targinfosize %u != %Zu\n",
++                     targinfosize,
++                     IP6T_ALIGN(sizeof(struct ip6t_route_target_info)));
++              return 0;
++      }
++
++      return 1;
++}
++
++
++static struct ip6t_target ip6t_route_reg = {
++      .name       = "ROUTE",
++      .target     = ip6t_route_target,
++      .checkentry = ip6t_route_checkentry,
++      .me         = THIS_MODULE
++};
++
++
++static int __init init(void)
++{
++      printk(KERN_DEBUG "registering ipv6 ROUTE target\n");
++      if (ip6t_register_target(&ip6t_route_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++
++static void __exit fini(void)
++{
++      ip6t_unregister_target(&ip6t_route_reg);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_TRACE.c linux-2.4.20/net/ipv6/netfilter/ip6t_TRACE.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_TRACE.c   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_TRACE.c       Wed Sep 24 09:18:14 2003
+@@ -0,0 +1,66 @@
++/* This is a module which is used for setting
++ * the NFC_TRACE flag in the nfcache field of an skb. 
++ */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++
++#include <linux/netfilter_ipv6/ip6_tables.h>
++
++MODULE_LICENSE("GPL");
++
++static unsigned int
++target(struct sk_buff **pskb,
++      unsigned int hooknum,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *targinfo,
++      void *userinfo)
++{
++      (*pskb)->nfcache |= NFC_TRACE;
++      return IP6T_CONTINUE;
++}
++
++static int 
++checkentry(const char *tablename,
++                 const struct ip6t_entry *e,
++           void *targinfo,
++           unsigned int targinfosize,
++           unsigned int hook_mask)
++{
++      if (targinfosize != 0) {
++              printk(KERN_WARNING "TRACE: targinfosize %u != 0\n",
++                     targinfosize);
++              return 0;
++      }
++
++      if (strcmp(tablename, "raw") != 0) {
++              printk(KERN_WARNING "TRACE: can only be called from \"raw\" table, not \"%s\"\n", tablename);
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ip6t_target ip6t_trace_reg = { 
++      .list = { NULL, NULL },
++      .name = "TRACE",
++      .target = target, 
++      .checkentry = checkentry, 
++      .destroy = NULL, 
++      .me = THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ip6t_register_target(&ip6t_trace_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ip6t_unregister_target(&ip6t_trace_reg);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_ah.c linux-2.4.20/net/ipv6/netfilter/ip6t_ah.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_ah.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_ah.c  Wed Sep 24 09:16:17 2003
+@@ -0,0 +1,207 @@
++/* Kernel module to match AH parameters. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ipv6.h>
++#include <linux/types.h>
++#include <net/checksum.h>
++#include <net/ipv6.h>
++
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_ah.h>
++
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("IPv6 AH match");
++MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++struct ahhdr {
++       __u8    nexthdr;
++       __u8    hdrlen;
++       __u16   reserved;
++       __u32   spi;
++};
++
++/* Returns 1 if the spi is matched by the range, 0 otherwise */
++static inline int
++spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
++{
++       int r=0;
++       DEBUGP("ah spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
++              min,spi,max);
++       r=(spi >= min && spi <= max) ^ invert;
++       DEBUGP(" result %s\n",r? "PASS\n" : "FAILED\n");
++       return r;
++}
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *protohdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++       struct ahhdr *ah = NULL;
++       const struct ip6t_ah *ahinfo = matchinfo;
++       unsigned int temp;
++       int len;
++       u8 nexthdr;
++       unsigned int ptr;
++       unsigned int hdrlen = 0;
++
++       /*DEBUGP("IPv6 AH entered\n");*/
++       /* if (opt->auth == 0) return 0;
++       * It does not filled on output */
++
++       /* type of the 1st exthdr */
++       nexthdr = skb->nh.ipv6h->nexthdr;
++       /* pointer to the 1st exthdr */
++       ptr = sizeof(struct ipv6hdr);
++       /* available length */
++       len = skb->len - ptr;
++       temp = 0;
++
++        while (ip6t_ext_hdr(nexthdr)) {
++               struct ipv6_opt_hdr *hdr;
++
++              DEBUGP("ipv6_ah header iteration \n");
++
++              /* Is there enough space for the next ext header? */
++                if (len < (int)sizeof(struct ipv6_opt_hdr))
++                        return 0;
++              /* No more exthdr -> evaluate */
++                if (nexthdr == NEXTHDR_NONE) {
++                     break;
++              }
++              /* ESP -> evaluate */
++                if (nexthdr == NEXTHDR_ESP) {
++                     break;
++              }
++
++              hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
++
++              /* Calculate the header length */
++                if (nexthdr == NEXTHDR_FRAGMENT) {
++                        hdrlen = 8;
++                } else if (nexthdr == NEXTHDR_AUTH)
++                        hdrlen = (hdr->hdrlen+2)<<2;
++                else
++                        hdrlen = ipv6_optlen(hdr);
++
++              /* AH -> evaluate */
++                if (nexthdr == NEXTHDR_AUTH) {
++                     temp |= MASK_AH;
++                     break;
++              }
++
++
++              /* set the flag */
++              switch (nexthdr){
++                     case NEXTHDR_HOP:
++                     case NEXTHDR_ROUTING:
++                     case NEXTHDR_FRAGMENT:
++                     case NEXTHDR_AUTH:
++                     case NEXTHDR_DEST:
++                            break;
++                     default:
++                            DEBUGP("ipv6_ah match: unknown nextheader %u\n",nexthdr);
++                            return 0;
++                            break;
++              }
++
++                nexthdr = hdr->nexthdr;
++                len -= hdrlen;
++                ptr += hdrlen;
++              if ( ptr > skb->len ) {
++                      DEBUGP("ipv6_ah: new pointer too large! \n");
++                      break;
++              }
++        }
++
++       /* AH header not found */
++       if ( temp != MASK_AH ) return 0;
++
++       if (len < (int)sizeof(struct ahhdr)){
++             *hotdrop = 1;
++                      return 0;
++       }
++
++       ah = (struct ahhdr *) (skb->data + ptr);
++
++       DEBUGP("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen);
++       DEBUGP("RES %04X ", ah->reserved);
++       DEBUGP("SPI %u %08X\n", ntohl(ah->spi), ntohl(ah->spi));
++
++       DEBUGP("IPv6 AH spi %02X ",
++                      (spi_match(ahinfo->spis[0], ahinfo->spis[1],
++                           ntohl(ah->spi),
++                           !!(ahinfo->invflags & IP6T_AH_INV_SPI))));
++       DEBUGP("len %02X %04X %02X ",
++                      ahinfo->hdrlen, hdrlen,
++                      (!ahinfo->hdrlen ||
++                           (ahinfo->hdrlen == hdrlen) ^
++                           !!(ahinfo->invflags & IP6T_AH_INV_LEN)));
++       DEBUGP("res %02X %04X %02X\n", 
++                      ahinfo->hdrres, ah->reserved,
++                      !(ahinfo->hdrres && ah->reserved));
++
++       return (ah != NULL)
++              &&
++              (spi_match(ahinfo->spis[0], ahinfo->spis[1],
++                           ntohl(ah->spi),
++                           !!(ahinfo->invflags & IP6T_AH_INV_SPI)))
++              &&
++              (!ahinfo->hdrlen ||
++                           (ahinfo->hdrlen == hdrlen) ^
++                           !!(ahinfo->invflags & IP6T_AH_INV_LEN))
++              &&
++              !(ahinfo->hdrres && ah->reserved);
++}
++
++/* Called when user tries to insert an entry of this type. */
++static int
++checkentry(const char *tablename,
++          const struct ip6t_ip6 *ip,
++          void *matchinfo,
++          unsigned int matchinfosize,
++          unsigned int hook_mask)
++{
++       const struct ip6t_ah *ahinfo = matchinfo;
++
++       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_ah))) {
++              DEBUGP("ip6t_ah: matchsize %u != %u\n",
++                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_ah)));
++              return 0;
++       }
++       if (ahinfo->invflags & ~IP6T_AH_INV_MASK) {
++              DEBUGP("ip6t_ah: unknown flags %X\n",
++                      ahinfo->invflags);
++              return 0;
++       }
++
++       return 1;
++}
++
++static struct ip6t_match ah_match
++= { { NULL, NULL }, "ah", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++       return ip6t_register_match(&ah_match);
++}
++
++static void __exit cleanup(void)
++{
++       ip6t_unregister_match(&ah_match);
++}
++
++module_init(init);
++module_exit(cleanup);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_condition.c linux-2.4.20/net/ipv6/netfilter/ip6t_condition.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_condition.c       Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_condition.c   Wed Sep 24 09:17:36 2003
+@@ -0,0 +1,254 @@
++/*-------------------------------------------*\
++|    Netfilter Condition Module for IPv6      |
++|                                             |
++|  Description: This module allows firewall   |
++|    rules to match using condition variables |
++|    stored in /proc files.                   |
++|                                             |
++|  Author: Stephane Ouellette     2003-02-10  |
++|          <ouellettes@videotron.ca>          |
++|                                             |
++|  This software is distributed under the     |
++|  terms of the GNU GPL.                      |
++\*-------------------------------------------*/
++
++#include<linux/module.h>
++#include<linux/proc_fs.h>
++#include<linux/spinlock.h>
++#include<linux/string.h>
++#include<asm/atomic.h>
++#include<linux/netfilter_ipv6/ip6_tables.h>
++#include<linux/netfilter_ipv6/ip6t_condition.h>
++
++
++#ifndef CONFIG_PROC_FS
++#error  "Proc file system support is required for this module"
++#endif
++
++
++MODULE_AUTHOR("Stephane Ouellette <ouellettes@videotron.ca>");
++MODULE_DESCRIPTION("Allows rules to match against condition variables");
++MODULE_LICENSE("GPL");
++
++
++struct condition_variable {
++      struct condition_variable *next;
++      struct proc_dir_entry *status_proc;
++      atomic_t refcount;
++        int enabled; /* TRUE == 1, FALSE == 0 */
++};
++
++
++static rwlock_t list_lock;
++static struct condition_variable *head = NULL;
++static struct proc_dir_entry *proc_net_condition = NULL;
++
++
++static int
++ipt_condition_read_info(char *buffer, char **start, off_t offset,
++                      int length, int *eof, void *data)
++{
++      struct condition_variable *var =
++          (struct condition_variable *) data;
++
++      if (offset == 0) {
++              *start = buffer;
++              buffer[0] = (var->enabled) ? '1' : '0';
++              buffer[1] = '\n';
++              return 2;
++      }
++
++      *eof = 1;
++      return 0;
++}
++
++
++static int
++ipt_condition_write_info(struct file *file, const char *buffer,
++                       unsigned long length, void *data)
++{
++      struct condition_variable *var =
++          (struct condition_variable *) data;
++
++      if (length) {
++              /* Match only on the first character */
++              switch (buffer[0]) {
++              case '0':
++                      var->enabled = 0;
++                      break;
++              case '1':
++                      var->enabled = 1;
++              }
++      }
++
++      return (int) length;
++}
++
++
++static int
++match(const struct sk_buff *skb, const struct net_device *in,
++      const struct net_device *out, const void *matchinfo, int offset,
++      const void *hdr, u_int16_t datalen, int *hotdrop)
++{
++      const struct condition6_info *info =
++          (const struct condition6_info *) matchinfo;
++      struct condition_variable *var;
++      int condition_status = 0;
++
++      read_lock(&list_lock);
++
++      for (var = head; var; var = var->next) {
++              if (strcmp(info->name, var->status_proc->name) == 0) {
++                      condition_status = var->enabled;
++                      break;
++              }
++      }
++
++      read_unlock(&list_lock);
++
++      return condition_status ^ info->invert;
++}
++
++
++
++static int
++checkentry(const char *tablename, const struct ip6t_ip6 *ip,
++         void *matchinfo, unsigned int matchsize, unsigned int hook_mask)
++{
++      struct condition6_info *info =
++          (struct condition6_info *) matchinfo;
++      struct condition_variable *var, *newvar;
++
++      if (matchsize != IP6T_ALIGN(sizeof(struct condition6_info)))
++              return 0;
++
++      /* The first step is to check if the condition variable already exists. */
++      /* Here, a read lock is sufficient because we won't change the list */
++      read_lock(&list_lock);
++
++      for (var = head; var; var = var->next) {
++              if (strcmp(info->name, var->status_proc->name) == 0) {
++                      atomic_inc(&var->refcount);
++                      read_unlock(&list_lock);
++                      return 1;
++              }
++      }
++
++      read_unlock(&list_lock);
++
++      /* At this point, we need to allocate a new condition variable */
++      newvar = kmalloc(sizeof(struct condition_variable), GFP_KERNEL);
++
++      if (!newvar)
++              return -ENOMEM;
++
++      /* Create the condition variable's proc file entry */
++      newvar->status_proc = create_proc_entry(info->name, 0644, proc_net_condition);
++
++      if (!newvar->status_proc) {
++        /*
++         * There are two possibilities:
++         *  1- Another condition variable with the same name has been created, which is valid.
++         *  2- There was a memory allocation error.
++         */
++              kfree(newvar);
++              read_lock(&list_lock);
++
++              for (var = head; var; var = var->next) {
++                      if (strcmp(info->name, var->status_proc->name) == 0) {
++                              atomic_inc(&var->refcount);
++                              read_unlock(&list_lock);
++                              return 1;
++                      }
++              }
++
++              read_unlock(&list_lock);
++              return -ENOMEM;
++      }
++
++      atomic_set(&newvar->refcount, 1);
++      newvar->enabled = 0;
++      newvar->status_proc->owner = THIS_MODULE;
++      newvar->status_proc->data = newvar;
++      wmb();
++      newvar->status_proc->read_proc = ipt_condition_read_info;
++      newvar->status_proc->write_proc = ipt_condition_write_info;
++
++      write_lock(&list_lock);
++
++      newvar->next = head;
++      head = newvar;
++
++      write_unlock(&list_lock);
++
++      return 1;
++}
++
++
++static void
++destroy(void *matchinfo, unsigned int matchsize)
++{
++      struct condition6_info *info =
++          (struct condition6_info *) matchinfo;
++      struct condition_variable *var, *prev = NULL;
++
++      if (matchsize != IP6T_ALIGN(sizeof(struct condition6_info)))
++              return;
++
++      write_lock(&list_lock);
++
++      for (var = head; var && strcmp(info->name, var->status_proc->name);
++           prev = var, var = var->next);
++
++      if (var && atomic_dec_and_test(&var->refcount)) {
++              if (prev)
++                      prev->next = var->next;
++              else
++                      head = var->next;
++
++              write_unlock(&list_lock);
++              remove_proc_entry(var->status_proc->name, proc_net_condition);
++              kfree(var);
++      } else
++              write_unlock(&list_lock);
++}
++
++
++static struct ip6t_match condition_match = {
++      .name = "condition",
++      .match = &match,
++      .checkentry = &checkentry,
++      .destroy = &destroy,
++      .me = THIS_MODULE
++};
++
++
++static int __init
++init(void)
++{
++      int errorcode;
++
++      rwlock_init(&list_lock);
++      proc_net_condition = proc_mkdir("ip6t_condition", proc_net);
++
++      if (proc_net_condition) {
++              errorcode = ipt_register_match(&condition_match);
++
++              if (errorcode)
++                      remove_proc_entry("ip6t_condition", proc_net);
++      } else
++              errorcode = -EACCES;
++
++      return errorcode;
++}
++
++
++static void __exit
++fini(void)
++{
++      ipt_unregister_match(&condition_match);
++      remove_proc_entry("ip6t_condition", proc_net);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_dst.c linux-2.4.20/net/ipv6/netfilter/ip6t_dst.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_dst.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_dst.c Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,276 @@
++/* Kernel module to match Hop-by-Hop and Destination parameters. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ipv6.h>
++#include <linux/types.h>
++#include <net/checksum.h>
++#include <net/ipv6.h>
++
++#include <asm/byteorder.h>
++
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_opts.h>
++
++#define LOW(n)                (n & 0x00FF)
++
++#define HOPBYHOP      0
++
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
++#if HOPBYHOP
++MODULE_DESCRIPTION("IPv6 HbH match");
++#else
++MODULE_DESCRIPTION("IPv6 DST match");
++#endif
++MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++/*
++ * (Type & 0xC0) >> 6
++ *    0       -> ignorable
++ *    1       -> must drop the packet
++ *    2       -> send ICMP PARM PROB regardless and drop packet
++ *    3       -> Send ICMP if not a multicast address and drop packet
++ *  (Type & 0x20) >> 5
++ *    0       -> invariant
++ *    1       -> can change the routing
++ *  (Type & 0x1F) Type
++ *      0     -> PAD0 (only 1 byte!)
++ *      1     -> PAD1 LENGTH info (total length = length + 2)
++ *      C0 | 2        -> JUMBO 4 x x x x ( xxxx > 64k )
++ *      5     -> RTALERT 2 x x
++ */
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *protohdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++       struct ipv6_opt_hdr *optsh = NULL;
++       const struct ip6t_opts *optinfo = matchinfo;
++       unsigned int temp;
++       unsigned int len;
++       u8 nexthdr;
++       unsigned int ptr;
++       unsigned int hdrlen = 0;
++       unsigned int ret = 0;
++       u_int16_t *optdesc = NULL;
++       
++       /* type of the 1st exthdr */
++       nexthdr = skb->nh.ipv6h->nexthdr;
++       /* pointer to the 1st exthdr */
++       ptr = sizeof(struct ipv6hdr);
++       /* available length */
++       len = skb->len - ptr;
++       temp = 0;
++
++        while (ip6t_ext_hdr(nexthdr)) {
++               struct ipv6_opt_hdr *hdr;
++
++              DEBUGP("ipv6_opts header iteration \n");
++
++              /* Is there enough space for the next ext header? */
++                if (len < (int)sizeof(struct ipv6_opt_hdr))
++                        return 0;
++              /* No more exthdr -> evaluate */
++                if (nexthdr == NEXTHDR_NONE) {
++                     break;
++              }
++              /* ESP -> evaluate */
++                if (nexthdr == NEXTHDR_ESP) {
++                     break;
++              }
++
++              hdr=(void *)(skb->data)+ptr;
++
++              /* Calculate the header length */
++                if (nexthdr == NEXTHDR_FRAGMENT) {
++                        hdrlen = 8;
++                } else if (nexthdr == NEXTHDR_AUTH)
++                        hdrlen = (hdr->hdrlen+2)<<2;
++                else
++                        hdrlen = ipv6_optlen(hdr);
++
++              /* OPTS -> evaluate */
++#if HOPBYHOP
++                if (nexthdr == NEXTHDR_HOP) {
++                     temp |= MASK_HOPOPTS;
++#else
++                if (nexthdr == NEXTHDR_DEST) {
++                     temp |= MASK_DSTOPTS;
++#endif
++                     break;
++              }
++
++
++              /* set the flag */
++              switch (nexthdr){
++                     case NEXTHDR_HOP:
++                     case NEXTHDR_ROUTING:
++                     case NEXTHDR_FRAGMENT:
++                     case NEXTHDR_AUTH:
++                     case NEXTHDR_DEST:
++                            break;
++                     default:
++                            DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr);
++                            return 0;
++                            break;
++              }
++
++                nexthdr = hdr->nexthdr;
++                len -= hdrlen;
++                ptr += hdrlen;
++              if ( ptr > skb->len ) {
++                      DEBUGP("ipv6_opts: new pointer is too large! \n");
++                      break;
++              }
++        }
++
++       /* OPTIONS header not found */
++#if HOPBYHOP
++       if ( temp != MASK_HOPOPTS ) return 0;
++#else
++       if ( temp != MASK_DSTOPTS ) return 0;
++#endif
++
++       if (len < (int)sizeof(struct ipv6_opt_hdr)){
++             *hotdrop = 1;
++                      return 0;
++       }
++
++       if (len < hdrlen){
++             /* Packet smaller than it's length field */
++                      return 0;
++       }
++
++       optsh=(void *)(skb->data)+ptr;
++
++       DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, optsh->hdrlen);
++
++       DEBUGP("len %02X %04X %02X ",
++                      optinfo->hdrlen, hdrlen,
++                      (!(optinfo->flags & IP6T_OPTS_LEN) ||
++                           ((optinfo->hdrlen == hdrlen) ^
++                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
++
++       ret = (optsh != NULL)
++                      &&
++              (!(optinfo->flags & IP6T_OPTS_LEN) ||
++                           ((optinfo->hdrlen == hdrlen) ^
++                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
++
++       temp = len = 0;
++       ptr += 2;
++       hdrlen -= 2;
++       if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){
++             return ret;
++      } else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
++              DEBUGP("Not strict - not implemented");
++      } else {
++              DEBUGP("Strict ");
++              DEBUGP("#%d ",optinfo->optsnr);
++              for(temp=0; temp<optinfo->optsnr; temp++){
++                      optdesc = (void *)(skb->data)+ptr;
++                      /* Type check */
++                      if ( (unsigned char)*optdesc != 
++                              (optinfo->opts[temp] & 0xFF00)>>8 ){
++                              DEBUGP("Tbad %02X %02X\n",
++                                              (unsigned char)*optdesc,
++                                              (optinfo->opts[temp] &
++                                               0xFF00)>>8);
++                              return 0;
++                      } else {
++                              DEBUGP("Tok ");
++                      }
++                      /* Length check */
++                      if (((optinfo->opts[temp] & 0x00FF) != 0xFF) &&
++                              (unsigned char)*optdesc != 0){
++                              if ( ntohs((u16)*optdesc) != 
++                                              optinfo->opts[temp] ){
++                                      DEBUGP("Lbad %02X %04X %04X\n",
++                                                      (unsigned char)*optdesc,
++                                                      ntohs((u16)*optdesc),
++                                                      optinfo->opts[temp]);
++                                      return 0;
++                              } else {
++                                      DEBUGP("Lok ");
++                              }
++                      }
++                      /* Step to the next */
++                      if ((unsigned char)*optdesc == 0){
++                              DEBUGP("PAD0 \n");
++                              ptr++;
++                              hdrlen--;
++                      } else {
++                              ptr += LOW(ntohs(*optdesc));
++                              hdrlen -= LOW(ntohs(*optdesc));
++                              DEBUGP("len%04X \n", 
++                                      LOW(ntohs(*optdesc)));
++                      }
++                      if (ptr > skb->len || ( !hdrlen && 
++                              (temp != optinfo->optsnr - 1))) {
++                              DEBUGP("new pointer is too large! \n");
++                              break;
++                      }
++              }
++              if (temp == optinfo->optsnr)
++                      return ret;
++              else return 0;
++      }
++
++      return 0;
++}
++
++/* Called when user tries to insert an entry of this type. */
++static int
++checkentry(const char *tablename,
++          const struct ip6t_ip6 *ip,
++          void *matchinfo,
++          unsigned int matchinfosize,
++          unsigned int hook_mask)
++{
++       const struct ip6t_opts *optsinfo = matchinfo;
++
++       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) {
++              DEBUGP("ip6t_opts: matchsize %u != %u\n",
++                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts)));
++              return 0;
++       }
++       if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
++              DEBUGP("ip6t_opts: unknown flags %X\n",
++                      optsinfo->invflags);
++              return 0;
++       }
++
++       return 1;
++}
++
++static struct ip6t_match opts_match
++#if HOPBYHOP
++= { { NULL, NULL }, "hbh", &match, &checkentry, NULL, THIS_MODULE };
++#else
++= { { NULL, NULL }, "dst", &match, &checkentry, NULL, THIS_MODULE };
++#endif
++
++static int __init init(void)
++{
++       return ip6t_register_match(&opts_match);
++}
++
++static void __exit cleanup(void)
++{
++       ip6t_unregister_match(&opts_match);
++}
++
++module_init(init);
++module_exit(cleanup);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_esp.c linux-2.4.20/net/ipv6/netfilter/ip6t_esp.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_esp.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_esp.c Wed Sep 24 09:16:17 2003
+@@ -0,0 +1,175 @@
++/* Kernel module to match ESP parameters. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ipv6.h>
++#include <linux/types.h>
++#include <net/checksum.h>
++#include <net/ipv6.h>
++
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_esp.h>
++
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("IPv6 ESP match");
++MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++struct esphdr {
++      __u32   spi;
++};
++
++/* Returns 1 if the spi is matched by the range, 0 otherwise */
++static inline int
++spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
++{
++      int r=0;
++        DEBUGP("esp spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
++              min,spi,max);
++      r=(spi >= min && spi <= max) ^ invert;
++      DEBUGP(" result %s\n",r? "PASS\n" : "FAILED\n");
++      return r;
++}
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *protohdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++      struct esphdr *esp = NULL;
++      const struct ip6t_esp *espinfo = matchinfo;
++      unsigned int temp;
++      int len;
++      u8 nexthdr;
++      unsigned int ptr;
++
++      /* Make sure this isn't an evil packet */
++      /*DEBUGP("ipv6_esp entered \n");*/
++
++      /* type of the 1st exthdr */
++      nexthdr = skb->nh.ipv6h->nexthdr;
++      /* pointer to the 1st exthdr */
++      ptr = sizeof(struct ipv6hdr);
++      /* available length */
++      len = skb->len - ptr;
++      temp = 0;
++
++        while (ip6t_ext_hdr(nexthdr)) {
++              struct ipv6_opt_hdr *hdr;
++              int hdrlen;
++
++              DEBUGP("ipv6_esp header iteration \n");
++
++              /* Is there enough space for the next ext header? */
++                if (len < (int)sizeof(struct ipv6_opt_hdr))
++                        return 0;
++              /* No more exthdr -> evaluate */
++                if (nexthdr == NEXTHDR_NONE) {
++                      break;
++              }
++              /* ESP -> evaluate */
++                if (nexthdr == NEXTHDR_ESP) {
++                      temp |= MASK_ESP;
++                      break;
++              }
++
++              hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
++
++              /* Calculate the header length */
++                if (nexthdr == NEXTHDR_FRAGMENT) {
++                        hdrlen = 8;
++                } else if (nexthdr == NEXTHDR_AUTH)
++                        hdrlen = (hdr->hdrlen+2)<<2;
++                else
++                        hdrlen = ipv6_optlen(hdr);
++
++              /* set the flag */
++              switch (nexthdr){
++                      case NEXTHDR_HOP:
++                      case NEXTHDR_ROUTING:
++                      case NEXTHDR_FRAGMENT:
++                      case NEXTHDR_AUTH:
++                      case NEXTHDR_DEST:
++                              break;
++                      default:
++                              DEBUGP("ipv6_esp match: unknown nextheader %u\n",nexthdr);
++                              return 0;
++                              break;
++              }
++
++                nexthdr = hdr->nexthdr;
++                len -= hdrlen;
++                ptr += hdrlen;
++              if ( ptr > skb->len ) {
++                      DEBUGP("ipv6_esp: new pointer too large! \n");
++                      break;
++              }
++        }
++
++      /* ESP header not found */
++      if ( temp != MASK_ESP ) return 0;
++
++       if (len < (int)sizeof(struct esphdr)){
++             *hotdrop = 1;
++                      return 0;
++       }
++
++      esp = (struct esphdr *) (skb->data + ptr);
++
++      DEBUGP("IPv6 ESP SPI %u %08X\n", ntohl(esp->spi), ntohl(esp->spi));
++
++      return (esp != NULL)
++              && spi_match(espinfo->spis[0], espinfo->spis[1],
++                            ntohl(esp->spi),
++                            !!(espinfo->invflags & IP6T_ESP_INV_SPI));
++}
++
++/* Called when user tries to insert an entry of this type. */
++static int
++checkentry(const char *tablename,
++         const struct ip6t_ip6 *ip,
++         void *matchinfo,
++         unsigned int matchinfosize,
++         unsigned int hook_mask)
++{
++      const struct ip6t_esp *espinfo = matchinfo;
++
++      if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_esp))) {
++              DEBUGP("ip6t_esp: matchsize %u != %u\n",
++                       matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_esp)));
++              return 0;
++      }
++      if (espinfo->invflags & ~IP6T_ESP_INV_MASK) {
++              DEBUGP("ip6t_esp: unknown flags %X\n",
++                       espinfo->invflags);
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ip6t_match esp_match
++= { { NULL, NULL }, "esp", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      return ip6t_register_match(&esp_match);
++}
++
++static void __exit cleanup(void)
++{
++      ip6t_unregister_match(&esp_match);
++}
++
++module_init(init);
++module_exit(cleanup);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_frag.c linux-2.4.20/net/ipv6/netfilter/ip6t_frag.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_frag.c    Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_frag.c        Wed Sep 24 09:16:17 2003
+@@ -0,0 +1,239 @@
++/* Kernel module to match FRAG parameters. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ipv6.h>
++#include <linux/types.h>
++#include <net/checksum.h>
++#include <net/ipv6.h>
++
++#include <asm/byteorder.h>
++
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_frag.h>
++
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("IPv6 FRAG match");
++MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++#if 0
++#if     BYTE_ORDER == BIG_ENDIAN
++#define IP6F_OFF_MASK       0xfff8  /* mask out offset from _offlg */
++#define IP6F_RESERVED_MASK  0x0006  /* reserved bits in ip6f_offlg */
++#define IP6F_MORE_FRAG      0x0001  /* more-fragments flag */
++#else   /* BYTE_ORDER == LITTLE_ENDIAN */
++#define IP6F_OFF_MASK       0xf8ff  /* mask out offset from _offlg */
++#define IP6F_RESERVED_MASK  0x0600  /* reserved bits in ip6f_offlg */
++#define IP6F_MORE_FRAG      0x0100  /* more-fragments flag */
++#endif
++#endif
++
++#define IP6F_OFF_MASK       0xf8ff  /* mask out offset from _offlg */
++#define IP6F_RESERVED_MASK  0x0600  /* reserved bits in ip6f_offlg */
++#define IP6F_MORE_FRAG      0x0100  /* more-fragments flag */
++
++struct fraghdr {
++       __u8    nexthdr;
++       __u8    hdrlen;
++       __u16   info;
++       __u32   id;
++};
++
++/* Returns 1 if the id is matched by the range, 0 otherwise */
++static inline int
++id_match(u_int32_t min, u_int32_t max, u_int32_t id, int invert)
++{
++       int r=0;
++       DEBUGP("frag id_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
++              min,id,max);
++       r=(id >= min && id <= max) ^ invert;
++       DEBUGP(" result %s\n",r? "PASS" : "FAILED");
++       return r;
++}
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *protohdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++       struct fraghdr *frag = NULL;
++       const struct ip6t_frag *fraginfo = matchinfo;
++       unsigned int temp;
++       int len;
++       u8 nexthdr;
++       unsigned int ptr;
++       unsigned int hdrlen = 0;
++
++       /* type of the 1st exthdr */
++       nexthdr = skb->nh.ipv6h->nexthdr;
++       /* pointer to the 1st exthdr */
++       ptr = sizeof(struct ipv6hdr);
++       /* available length */
++       len = skb->len - ptr;
++       temp = 0;
++
++        while (ip6t_ext_hdr(nexthdr)) {
++               struct ipv6_opt_hdr *hdr;
++
++              DEBUGP("ipv6_frag header iteration \n");
++
++              /* Is there enough space for the next ext header? */
++                if (len < (int)sizeof(struct ipv6_opt_hdr))
++                        return 0;
++              /* No more exthdr -> evaluate */
++                if (nexthdr == NEXTHDR_NONE) {
++                     break;
++              }
++              /* ESP -> evaluate */
++                if (nexthdr == NEXTHDR_ESP) {
++                     break;
++              }
++
++              hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
++
++              /* Calculate the header length */
++                if (nexthdr == NEXTHDR_FRAGMENT) {
++                        hdrlen = 8;
++                } else if (nexthdr == NEXTHDR_AUTH)
++                        hdrlen = (hdr->hdrlen+2)<<2;
++                else
++                        hdrlen = ipv6_optlen(hdr);
++
++              /* FRAG -> evaluate */
++                if (nexthdr == NEXTHDR_FRAGMENT) {
++                     temp |= MASK_FRAGMENT;
++                     break;
++              }
++
++
++              /* set the flag */
++              switch (nexthdr){
++                     case NEXTHDR_HOP:
++                     case NEXTHDR_ROUTING:
++                     case NEXTHDR_FRAGMENT:
++                     case NEXTHDR_AUTH:
++                     case NEXTHDR_DEST:
++                            break;
++                     default:
++                            DEBUGP("ipv6_frag match: unknown nextheader %u\n",nexthdr);
++                            return 0;
++                            break;
++              }
++
++                nexthdr = hdr->nexthdr;
++                len -= hdrlen;
++                ptr += hdrlen;
++              if ( ptr > skb->len ) {
++                      DEBUGP("ipv6_frag: new pointer too large! \n");
++                      break;
++              }
++        }
++
++       /* FRAG header not found */
++       if ( temp != MASK_FRAGMENT ) return 0;
++
++       if (len < (int)sizeof(struct fraghdr)){
++             *hotdrop = 1;
++                      return 0;
++       }
++
++       frag = (struct fraghdr *) (skb->data + ptr);
++
++       DEBUGP("IPv6 FRAG LEN %u %u ", hdrlen, frag->hdrlen);
++       DEBUGP("INFO %04X ", frag->info);
++       DEBUGP("OFFSET %04X ", frag->info & IP6F_OFF_MASK);
++       DEBUGP("RES %04X ", frag->info & IP6F_RESERVED_MASK);
++       DEBUGP("MF %04X ", frag->info & IP6F_MORE_FRAG);
++       DEBUGP("ID %u %08X\n", ntohl(frag->id), ntohl(frag->id));
++
++       DEBUGP("IPv6 FRAG id %02X ",
++                      (id_match(fraginfo->ids[0], fraginfo->ids[1],
++                           ntohl(frag->id),
++                           !!(fraginfo->invflags & IP6T_FRAG_INV_IDS))));
++       DEBUGP("len %02X %04X %02X ",
++                      fraginfo->hdrlen, hdrlen,
++                      (!fraginfo->hdrlen ||
++                           (fraginfo->hdrlen == hdrlen) ^
++                           !!(fraginfo->invflags & IP6T_FRAG_INV_LEN)));
++       DEBUGP("res %02X %02X %02X ", 
++                      (fraginfo->flags & IP6T_FRAG_RES), frag->info & IP6F_RESERVED_MASK,
++                      !((fraginfo->flags & IP6T_FRAG_RES) && (frag->info & IP6F_RESERVED_MASK)));
++       DEBUGP("first %02X %02X %02X ", 
++                      (fraginfo->flags & IP6T_FRAG_FST), frag->info & IP6F_OFF_MASK,
++                      !((fraginfo->flags & IP6T_FRAG_FST) && (frag->info & IP6F_OFF_MASK)));
++       DEBUGP("mf %02X %02X %02X ", 
++                      (fraginfo->flags & IP6T_FRAG_MF), frag->info & IP6F_MORE_FRAG,
++                      !((fraginfo->flags & IP6T_FRAG_MF) && !((frag->info & IP6F_MORE_FRAG))));
++       DEBUGP("last %02X %02X %02X\n", 
++                      (fraginfo->flags & IP6T_FRAG_NMF), frag->info & IP6F_MORE_FRAG,
++                      !((fraginfo->flags & IP6T_FRAG_NMF) && (frag->info & IP6F_MORE_FRAG)));
++
++       return (frag != NULL)
++                      &&
++                      (id_match(fraginfo->ids[0], fraginfo->ids[1],
++                           ntohl(frag->id),
++                           !!(fraginfo->invflags & IP6T_FRAG_INV_IDS)))
++              &&
++              (!fraginfo->hdrlen ||
++                           (fraginfo->hdrlen == hdrlen) ^
++                           !!(fraginfo->invflags & IP6T_FRAG_INV_LEN))
++              &&
++              !((fraginfo->flags & IP6T_FRAG_RES) && (frag->info & IP6F_RESERVED_MASK))
++              &&
++              !((fraginfo->flags & IP6T_FRAG_FST) && (frag->info & IP6F_OFF_MASK))
++              &&
++              !((fraginfo->flags & IP6T_FRAG_MF) && !((frag->info & IP6F_MORE_FRAG)))
++              &&
++              !((fraginfo->flags & IP6T_FRAG_NMF) && (frag->info & IP6F_MORE_FRAG));
++}
++
++/* Called when user tries to insert an entry of this type. */
++static int
++checkentry(const char *tablename,
++          const struct ip6t_ip6 *ip,
++          void *matchinfo,
++          unsigned int matchinfosize,
++          unsigned int hook_mask)
++{
++       const struct ip6t_frag *fraginfo = matchinfo;
++
++       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_frag))) {
++              DEBUGP("ip6t_frag: matchsize %u != %u\n",
++                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_frag)));
++              return 0;
++       }
++       if (fraginfo->invflags & ~IP6T_FRAG_INV_MASK) {
++              DEBUGP("ip6t_frag: unknown flags %X\n",
++                      fraginfo->invflags);
++              return 0;
++       }
++
++       return 1;
++}
++
++static struct ip6t_match frag_match
++= { { NULL, NULL }, "frag", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++       return ip6t_register_match(&frag_match);
++}
++
++static void __exit cleanup(void)
++{
++       ip6t_unregister_match(&frag_match);
++}
++
++module_init(init);
++module_exit(cleanup);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_fuzzy.c linux-2.4.20/net/ipv6/netfilter/ip6t_fuzzy.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_fuzzy.c   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_fuzzy.c       Wed Sep 24 09:16:45 2003
+@@ -0,0 +1,189 @@
++/*
++ * This module implements a simple TSK FLC
++ * (Takagi-Sugeno-Kang Fuzzy Logic Controller) that aims
++ * to limit , in an adaptive and flexible way , the packet rate crossing
++ * a given stream . It serves as an initial and very simple (but effective)
++ * example of how Fuzzy Logic techniques can be applied to defeat DoS attacks.
++ *  As a matter of fact , Fuzzy Logic can help us to insert any "behavior"
++ * into our code in a precise , adaptive and efficient manner.
++ *  The goal is very similar to that of "limit" match , but using techniques of
++ * Fuzzy Control , that allow us to shape the transfer functions precisely ,
++ * avoiding over and undershoots - and stuff like that .
++ *
++ *
++ * 2002-08-10  Hime Aguiar e Oliveira Jr. <hime@engineer.com> : Initial version.
++ * 2002-08-17  : Changed to eliminate floating point operations .
++ * 2002-08-23  : Coding style changes .
++ * 2003-04-08  Maciej Soltysiak <solt@dns.toxicilms.tv> : IPv6 Port
++ */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ipv6.h>
++#include <linux/random.h>
++#include <net/tcp.h>
++#include <linux/spinlock.h>
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_fuzzy.h>
++
++/*
++ Packet Acceptance Rate - LOW and Packet Acceptance Rate - HIGH
++ Expressed in percentage
++*/
++
++#define PAR_LOW               1/100
++#define PAR_HIGH      1
++
++static spinlock_t fuzzy_lock = SPIN_LOCK_UNLOCKED;
++
++MODULE_AUTHOR("Hime Aguiar e Oliveira Junior <hime@engineer.com>");
++MODULE_DESCRIPTION("IP tables Fuzzy Logic Controller match module");
++MODULE_LICENSE("GPL");
++
++static  u_int8_t mf_high(u_int32_t tx,u_int32_t mini,u_int32_t maxi)
++{
++      if (tx >= maxi) return 100;
++
++      if (tx <= mini) return 0;
++
++      return ((100 * (tx-mini)) / (maxi-mini));
++}
++
++static u_int8_t mf_low(u_int32_t tx,u_int32_t mini,u_int32_t maxi)
++{
++      if (tx <= mini) return 100;
++
++      if (tx >= maxi) return 0;
++
++      return ((100 * (maxi - tx)) / (maxi - mini));
++
++}
++
++static int
++ip6t_fuzzy_match(const struct sk_buff *pskb,
++             const struct net_device *in,
++             const struct net_device *out,
++             const void *matchinfo,
++             int offset,
++             const void *hdr,
++             u_int16_t datalen,
++             int *hotdrop)
++{
++      /* From userspace */
++
++      struct ip6t_fuzzy_info *info = (struct ip6t_fuzzy_info *) matchinfo;
++
++      u_int8_t random_number;
++      unsigned long amount;
++      u_int8_t howhigh, howlow;
++
++
++      spin_lock_bh(&fuzzy_lock); /* Rise the lock */
++
++      info->bytes_total += pskb->len;
++      info->packets_total++;
++
++      info->present_time = jiffies;
++
++      if (info->present_time >= info->previous_time)
++              amount = info->present_time - info->previous_time;
++      else {
++              /* There was a transition : I choose to re-sample
++                 and keep the old acceptance rate...
++              */
++
++              amount = 0;
++              info->previous_time = info->present_time;
++              info->bytes_total = info->packets_total = 0;
++           };
++
++      if ( amount > HZ/10) {/* More than 100 ms elapsed ... */
++
++              info->mean_rate = (u_int32_t) ((HZ * info->packets_total) \
++                                      / amount);
++
++              info->previous_time = info->present_time;
++              info->bytes_total = info->packets_total = 0;
++
++              howhigh = mf_high(info->mean_rate,info->minimum_rate,info->maximum_rate);
++              howlow  = mf_low(info->mean_rate,info->minimum_rate,info->maximum_rate);
++
++              info->acceptance_rate = (u_int8_t) \
++                              (howhigh * PAR_LOW + PAR_HIGH * howlow);
++
++      /* In fact, the above defuzzification would require a denominator
++       * proportional to (howhigh+howlow) but, in this particular case,
++       * that expression is constant.
++       * An imediate consequence is that it is not necessary to call
++       * both mf_high and mf_low - but to keep things understandable,
++       * I did so.
++       */
++
++      }
++
++      spin_unlock_bh(&fuzzy_lock); /* Release the lock */
++
++
++      if (info->acceptance_rate < 100)
++      {
++              get_random_bytes((void *)(&random_number), 1);
++
++              /*  If within the acceptance , it can pass => don't match */
++              if (random_number <= (255 * info->acceptance_rate) / 100)
++                      return 0;
++              else
++                      return 1; /* It can't pass (It matches) */
++      };
++
++      return 0; /* acceptance_rate == 100 % => Everything passes ... */
++
++}
++
++static int
++ip6t_fuzzy_checkentry(const char *tablename,
++                 const struct ip6t_ip6 *ip,
++                 void *matchinfo,
++                 unsigned int matchsize,
++                 unsigned int hook_mask)
++{
++
++      const struct ip6t_fuzzy_info *info = matchinfo;
++
++      if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_fuzzy_info))) {
++              printk("ip6t_fuzzy: matchsize %u != %u\n", matchsize,
++                     IP6T_ALIGN(sizeof(struct ip6t_fuzzy_info)));
++              return 0;
++      }
++
++      if ((info->minimum_rate < MINFUZZYRATE) || (info->maximum_rate > MAXFUZZYRATE)
++       || (info->minimum_rate >= info->maximum_rate)) {
++              printk("ip6t_fuzzy: BAD limits , please verify !!!\n");
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ip6t_match ip6t_fuzzy_reg = {
++      {NULL, NULL},
++      "fuzzy",
++      ip6t_fuzzy_match,
++      ip6t_fuzzy_checkentry,
++      NULL,
++      THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ip6t_register_match(&ip6t_fuzzy_reg))
++              return -EINVAL;
++
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ip6t_unregister_match(&ip6t_fuzzy_reg);
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_hbh.c linux-2.4.20/net/ipv6/netfilter/ip6t_hbh.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_hbh.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_hbh.c Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,276 @@
++/* Kernel module to match Hop-by-Hop and Destination parameters. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ipv6.h>
++#include <linux/types.h>
++#include <net/checksum.h>
++#include <net/ipv6.h>
++
++#include <asm/byteorder.h>
++
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_opts.h>
++
++#define LOW(n)                (n & 0x00FF)
++
++#define HOPBYHOP      1
++
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
++#if HOPBYHOP
++MODULE_DESCRIPTION("IPv6 HbH match");
++#else
++MODULE_DESCRIPTION("IPv6 DST match");
++#endif
++MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++/*
++ * (Type & 0xC0) >> 6
++ *    0       -> ignorable
++ *    1       -> must drop the packet
++ *    2       -> send ICMP PARM PROB regardless and drop packet
++ *    3       -> Send ICMP if not a multicast address and drop packet
++ *  (Type & 0x20) >> 5
++ *    0       -> invariant
++ *    1       -> can change the routing
++ *  (Type & 0x1F) Type
++ *      0     -> PAD0 (only 1 byte!)
++ *      1     -> PAD1 LENGTH info (total length = length + 2)
++ *      C0 | 2        -> JUMBO 4 x x x x ( xxxx > 64k )
++ *      5     -> RTALERT 2 x x
++ */
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *protohdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++       struct ipv6_opt_hdr *optsh = NULL;
++       const struct ip6t_opts *optinfo = matchinfo;
++       unsigned int temp;
++       unsigned int len;
++       u8 nexthdr;
++       unsigned int ptr;
++       unsigned int hdrlen = 0;
++       unsigned int ret = 0;
++       u_int16_t *optdesc = NULL;
++       
++       /* type of the 1st exthdr */
++       nexthdr = skb->nh.ipv6h->nexthdr;
++       /* pointer to the 1st exthdr */
++       ptr = sizeof(struct ipv6hdr);
++       /* available length */
++       len = skb->len - ptr;
++       temp = 0;
++
++        while (ip6t_ext_hdr(nexthdr)) {
++               struct ipv6_opt_hdr *hdr;
++
++              DEBUGP("ipv6_opts header iteration \n");
++
++              /* Is there enough space for the next ext header? */
++                if (len < (int)sizeof(struct ipv6_opt_hdr))
++                        return 0;
++              /* No more exthdr -> evaluate */
++                if (nexthdr == NEXTHDR_NONE) {
++                     break;
++              }
++              /* ESP -> evaluate */
++                if (nexthdr == NEXTHDR_ESP) {
++                     break;
++              }
++
++              hdr=(void *)(skb->data)+ptr;
++
++              /* Calculate the header length */
++                if (nexthdr == NEXTHDR_FRAGMENT) {
++                        hdrlen = 8;
++                } else if (nexthdr == NEXTHDR_AUTH)
++                        hdrlen = (hdr->hdrlen+2)<<2;
++                else
++                        hdrlen = ipv6_optlen(hdr);
++
++              /* OPTS -> evaluate */
++#if HOPBYHOP
++                if (nexthdr == NEXTHDR_HOP) {
++                     temp |= MASK_HOPOPTS;
++#else
++                if (nexthdr == NEXTHDR_DEST) {
++                     temp |= MASK_DSTOPTS;
++#endif
++                     break;
++              }
++
++
++              /* set the flag */
++              switch (nexthdr){
++                     case NEXTHDR_HOP:
++                     case NEXTHDR_ROUTING:
++                     case NEXTHDR_FRAGMENT:
++                     case NEXTHDR_AUTH:
++                     case NEXTHDR_DEST:
++                            break;
++                     default:
++                            DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr);
++                            return 0;
++                            break;
++              }
++
++                nexthdr = hdr->nexthdr;
++                len -= hdrlen;
++                ptr += hdrlen;
++              if ( ptr > skb->len ) {
++                      DEBUGP("ipv6_opts: new pointer is too large! \n");
++                      break;
++              }
++        }
++
++       /* OPTIONS header not found */
++#if HOPBYHOP
++       if ( temp != MASK_HOPOPTS ) return 0;
++#else
++       if ( temp != MASK_DSTOPTS ) return 0;
++#endif
++
++       if (len < (int)sizeof(struct ipv6_opt_hdr)){
++             *hotdrop = 1;
++                      return 0;
++       }
++
++       if (len < hdrlen){
++             /* Packet smaller than it's length field */
++                      return 0;
++       }
++
++       optsh=(void *)(skb->data)+ptr;
++
++       DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, optsh->hdrlen);
++
++       DEBUGP("len %02X %04X %02X ",
++                      optinfo->hdrlen, hdrlen,
++                      (!(optinfo->flags & IP6T_OPTS_LEN) ||
++                           ((optinfo->hdrlen == hdrlen) ^
++                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
++
++       ret = (optsh != NULL)
++                      &&
++              (!(optinfo->flags & IP6T_OPTS_LEN) ||
++                           ((optinfo->hdrlen == hdrlen) ^
++                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
++
++       temp = len = 0;
++       ptr += 2;
++       hdrlen -= 2;
++       if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){
++             return ret;
++      } else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
++              DEBUGP("Not strict - not implemented");
++      } else {
++              DEBUGP("Strict ");
++              DEBUGP("#%d ",optinfo->optsnr);
++              for(temp=0; temp<optinfo->optsnr; temp++){
++                      optdesc = (void *)(skb->data)+ptr;
++                      /* Type check */
++                      if ( (unsigned char)*optdesc != 
++                              (optinfo->opts[temp] & 0xFF00)>>8 ){
++                              DEBUGP("Tbad %02X %02X\n",
++                                              (unsigned char)*optdesc,
++                                              (optinfo->opts[temp] &
++                                               0xFF00)>>8);
++                              return 0;
++                      } else {
++                              DEBUGP("Tok ");
++                      }
++                      /* Length check */
++                      if (((optinfo->opts[temp] & 0x00FF) != 0xFF) &&
++                              (unsigned char)*optdesc != 0){
++                              if ( ntohs((u16)*optdesc) != 
++                                              optinfo->opts[temp] ){
++                                      DEBUGP("Lbad %02X %04X %04X\n",
++                                                      (unsigned char)*optdesc,
++                                                      ntohs((u16)*optdesc),
++                                                      optinfo->opts[temp]);
++                                      return 0;
++                              } else {
++                                      DEBUGP("Lok ");
++                              }
++                      }
++                      /* Step to the next */
++                      if ((unsigned char)*optdesc == 0){
++                              DEBUGP("PAD0 \n");
++                              ptr++;
++                              hdrlen--;
++                      } else {
++                              ptr += LOW(ntohs(*optdesc));
++                              hdrlen -= LOW(ntohs(*optdesc));
++                              DEBUGP("len%04X \n", 
++                                      LOW(ntohs(*optdesc)));
++                      }
++                      if (ptr > skb->len || ( !hdrlen && 
++                              (temp != optinfo->optsnr - 1))) {
++                              DEBUGP("new pointer is too large! \n");
++                              break;
++                      }
++              }
++              if (temp == optinfo->optsnr)
++                      return ret;
++              else return 0;
++      }
++
++      return 0;
++}
++
++/* Called when user tries to insert an entry of this type. */
++static int
++checkentry(const char *tablename,
++          const struct ip6t_ip6 *ip,
++          void *matchinfo,
++          unsigned int matchinfosize,
++          unsigned int hook_mask)
++{
++       const struct ip6t_opts *optsinfo = matchinfo;
++
++       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) {
++              DEBUGP("ip6t_opts: matchsize %u != %u\n",
++                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts)));
++              return 0;
++       }
++       if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
++              DEBUGP("ip6t_opts: unknown flags %X\n",
++                      optsinfo->invflags);
++              return 0;
++       }
++
++       return 1;
++}
++
++static struct ip6t_match opts_match
++#if HOPBYHOP
++= { { NULL, NULL }, "hbh", &match, &checkentry, NULL, THIS_MODULE };
++#else
++= { { NULL, NULL }, "dst", &match, &checkentry, NULL, THIS_MODULE };
++#endif
++
++static int __init init(void)
++{
++       return ip6t_register_match(&opts_match);
++}
++
++static void __exit cleanup(void)
++{
++       ip6t_unregister_match(&opts_match);
++}
++
++module_init(init);
++module_exit(cleanup);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_hl.c linux-2.4.20/net/ipv6/netfilter/ip6t_hl.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_hl.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_hl.c  Wed Sep 24 09:16:14 2003
+@@ -0,0 +1,74 @@
++/*
++ * Hop Limit matching module
++ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
++ * Based on HW's ttl module
++ *
++ * This software is distributed under the terms  GNU GPL
++ */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++
++#include <linux/netfilter_ipv6/ip6t_hl.h>
++#include <linux/netfilter_ipv6/ip6_tables.h>
++
++MODULE_AUTHOR("Maciej Soltysiak <solt@dns.toxicfilms.tv>");
++MODULE_DESCRIPTION("IP tables Hop Limit matching module");
++MODULE_LICENSE("GPL");
++
++static int match(const struct sk_buff *skb, const struct net_device *in,
++               const struct net_device *out, const void *matchinfo,
++               int offset, const void *hdr, u_int16_t datalen,
++               int *hotdrop)
++{
++      const struct ip6t_hl_info *info = matchinfo;
++      const struct ipv6hdr *ip6h = skb->nh.ipv6h;
++
++      switch (info->mode) {
++              case IP6T_HL_EQ:
++                      return (ip6h->hop_limit == info->hop_limit);
++                      break;
++              case IP6T_HL_NE:
++                      return (!(ip6h->hop_limit == info->hop_limit));
++                      break;
++              case IP6T_HL_LT:
++                      return (ip6h->hop_limit < info->hop_limit);
++                      break;
++              case IP6T_HL_GT:
++                      return (ip6h->hop_limit > info->hop_limit);
++                      break;
++              default:
++                      printk(KERN_WARNING "ip6t_hl: unknown mode %d\n", 
++                              info->mode);
++                      return 0;
++      }
++
++      return 0;
++}
++
++static int checkentry(const char *tablename, const struct ip6t_ip6 *ip,
++                    void *matchinfo, unsigned int matchsize,
++                    unsigned int hook_mask)
++{
++      if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_hl_info)))
++              return 0;
++
++      return 1;
++}
++
++static struct ip6t_match hl_match = { { NULL, NULL }, "hl", &match,
++              &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++      return ip6t_register_match(&hl_match);
++}
++
++static void __exit fini(void)
++{
++      ip6t_unregister_match(&hl_match);
++
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_ipv6header.c linux-2.4.20/net/ipv6/netfilter/ip6t_ipv6header.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_ipv6header.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_ipv6header.c  Wed Sep 24 09:16:26 2003
+@@ -0,0 +1,153 @@
++/* ipv6header match - matches IPv6 packets based
++on whether they contain certain headers */
++
++/* Original idea: Brad Chapman 
++ * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ipv6.h>
++#include <linux/types.h>
++#include <net/checksum.h>
++#include <net/ipv6.h>
++
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_ipv6header.h>
++
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("IPv6 headers match");
++MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
++
++static int
++ipv6header_match(const struct sk_buff *skb,
++               const struct net_device *in,
++               const struct net_device *out,
++               const void *matchinfo,
++               int offset,
++               const void *protohdr,
++               u_int16_t datalen,
++               int *hotdrop)
++{
++      const struct ip6t_ipv6header_info *info = matchinfo;
++      unsigned int temp;
++      int len;
++      u8 nexthdr;
++      unsigned int ptr;
++
++      /* Make sure this isn't an evil packet */
++
++      /* type of the 1st exthdr */
++      nexthdr = skb->nh.ipv6h->nexthdr;
++      /* pointer to the 1st exthdr */
++      ptr = sizeof(struct ipv6hdr);
++      /* available length */
++      len = skb->len - ptr;
++      temp = 0;
++
++        while (ip6t_ext_hdr(nexthdr)) {
++              struct ipv6_opt_hdr *hdr;
++              int hdrlen;
++
++              /* Is there enough space for the next ext header? */
++                if (len < (int)sizeof(struct ipv6_opt_hdr))
++                        return 0;
++              /* No more exthdr -> evaluate */
++                if (nexthdr == NEXTHDR_NONE) {
++                      temp |= MASK_NONE;
++                      break;
++              }
++              /* ESP -> evaluate */
++                if (nexthdr == NEXTHDR_ESP) {
++                      temp |= MASK_ESP;
++                      break;
++              }
++
++              hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
++
++              /* Calculate the header length */
++                if (nexthdr == NEXTHDR_FRAGMENT) {
++                        hdrlen = 8;
++                } else if (nexthdr == NEXTHDR_AUTH)
++                        hdrlen = (hdr->hdrlen+2)<<2;
++                else
++                        hdrlen = ipv6_optlen(hdr);
++
++              /* set the flag */
++              switch (nexthdr){
++                      case NEXTHDR_HOP:
++                              temp |= MASK_HOPOPTS;
++                              break;
++                      case NEXTHDR_ROUTING:
++                              temp |= MASK_ROUTING;
++                              break;
++                      case NEXTHDR_FRAGMENT:
++                              temp |= MASK_FRAGMENT;
++                              break;
++                      case NEXTHDR_AUTH:
++                              temp |= MASK_AH;
++                              break;
++                      case NEXTHDR_DEST:
++                              temp |= MASK_DSTOPTS;
++                              break;
++                      default:
++                              return 0;
++                              break;
++              }
++
++                nexthdr = hdr->nexthdr;
++                len -= hdrlen;
++                ptr += hdrlen;
++              if ( ptr > skb->len ) {
++                      break;
++              }
++        }
++
++      if ( (nexthdr != NEXTHDR_NONE ) && (nexthdr != NEXTHDR_ESP) )
++              temp |= MASK_PROTO;
++
++      if (info->modeflag)
++              return (!( (temp & info->matchflags)
++                      ^ info->matchflags) ^ info->invflags);
++      else
++              return (!( temp ^ info->matchflags) ^ info->invflags);
++}
++
++static int
++ipv6header_checkentry(const char *tablename,
++                    const struct ip6t_ip6 *ip,
++                    void *matchinfo,
++                    unsigned int matchsize,
++                    unsigned int hook_mask)
++{
++      /* Check for obvious errors */
++      /* This match is valid in all hooks! */
++      if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info))) {
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ip6t_match
++ip6t_ipv6header_match = {
++      { NULL, NULL },
++      "ipv6header",
++      &ipv6header_match,
++      &ipv6header_checkentry,
++      THIS_MODULE
++};
++
++static int  __init ipv6header_init(void)
++{
++      return ip6t_register_match(&ip6t_ipv6header_match);
++}
++
++static void __exit ipv6header_exit(void)
++{
++      ip6t_unregister_match(&ip6t_ipv6header_match);
++}
++
++module_init(ipv6header_init);
++module_exit(ipv6header_exit);
++
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_nth.c linux-2.4.20/net/ipv6/netfilter/ip6t_nth.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_nth.c     Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_nth.c Wed Sep 24 09:16:55 2003
+@@ -0,0 +1,173 @@
++/*
++  This is a module which is used for match support for every Nth packet
++  This file is distributed under the terms of the GNU General Public
++  License (GPL). Copies of the GPL can be obtained from:
++     ftp://prep.ai.mit.edu/pub/gnu/GPL
++
++  2001-07-18 Fabrice MARIE <fabrice@netfilter.org> : initial implementation.
++  2001-09-20 Richard Wagner (rwagner@cloudnet.com)
++        * added support for multiple counters
++        * added support for matching on individual packets
++          in the counter cycle
++  2003-04-30 Maciej Soltysiak <solt@dns.toxicfilms.tv> : IPv6 Port
++
++*/
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <net/tcp.h>
++#include <linux/spinlock.h>
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_nth.h>
++
++MODULE_LICENSE("GPL");
++
++/*
++ * State information.
++ */
++struct state {
++      spinlock_t lock;
++      u_int16_t number;
++};
++
++static struct state states[IP6T_NTH_NUM_COUNTERS];
++
++static int
++ip6t_nth_match(const struct sk_buff *pskb,
++            const struct net_device *in,
++            const struct net_device *out,
++            const void *matchinfo,
++            int offset,
++            const void *hdr,
++            u_int16_t datalen,
++            int *hotdrop)
++{
++      /* Parameters from userspace */
++      const struct ip6t_nth_info *info = matchinfo;
++        unsigned counter = info->counter;
++              if((counter < 0) || (counter >= IP6T_NTH_NUM_COUNTERS)) 
++              {
++                      printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IP6T_NTH_NUM_COUNTERS-1);
++               return 0;
++        };
++
++        spin_lock(&states[counter].lock);
++
++        /* Are we matching every nth packet?*/
++        if (info->packet == 0xFF)
++        {
++              /* We're matching every nth packet and only every nth packet*/
++              /* Do we match or invert match? */
++              if (info->not == 0)
++              {
++                      if (states[counter].number == 0)
++                      {
++                              ++states[counter].number;
++                              goto match;
++                      }
++                      if (states[counter].number >= info->every)
++                              states[counter].number = 0; /* reset the counter */
++                      else
++                              ++states[counter].number;
++                      goto dontmatch;
++              }
++              else
++              {
++                      if (states[counter].number == 0)
++                      {
++                              ++states[counter].number;
++                              goto dontmatch;
++                      }
++                      if (states[counter].number >= info->every)
++                              states[counter].number = 0;
++                      else
++                              ++states[counter].number;
++                      goto match;
++              }
++        }
++        else
++        {
++              /* We're using the --packet, so there must be a rule for every value */
++              if (states[counter].number == info->packet)
++              {
++                      /* only increment the counter when a match happens */
++                      if (states[counter].number >= info->every)
++                              states[counter].number = 0; /* reset the counter */
++                      else
++                              ++states[counter].number;
++                      goto match;
++              }
++              else
++                      goto dontmatch;
++      }
++
++ dontmatch:
++      /* don't match */
++      spin_unlock(&states[counter].lock);
++      return 0;
++
++ match:
++      spin_unlock(&states[counter].lock);
++      return 1;
++}
++
++static int
++ip6t_nth_checkentry(const char *tablename,
++                 const struct ip6t_ip6 *e,
++                 void *matchinfo,
++                 unsigned int matchsize,
++                 unsigned int hook_mask)
++{
++      /* Parameters from userspace */
++      const struct ip6t_nth_info *info = matchinfo;
++        unsigned counter = info->counter;
++        if((counter < 0) || (counter >= IP6T_NTH_NUM_COUNTERS)) 
++      {
++              printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IP6T_NTH_NUM_COUNTERS-1);
++                      return 0;
++              };
++
++      if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_nth_info))) {
++              printk("nth: matchsize %u != %u\n", matchsize,
++                     IP6T_ALIGN(sizeof(struct ip6t_nth_info)));
++              return 0;
++      }
++
++      states[counter].number = info->startat;
++
++      return 1;
++}
++
++static struct ip6t_match ip6t_nth_reg = { 
++      {NULL, NULL},
++      "nth",
++      ip6t_nth_match,
++      ip6t_nth_checkentry,
++      NULL,
++      THIS_MODULE };
++
++static int __init init(void)
++{
++      unsigned counter;
++        memset(&states, 0, sizeof(states));
++      if (ip6t_register_match(&ip6t_nth_reg))
++              return -EINVAL;
++
++        for(counter = 0; counter < IP6T_NTH_NUM_COUNTERS; counter++) 
++      {
++              spin_lock_init(&(states[counter].lock));
++        };
++
++      printk("ip6t_nth match loaded\n");
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ip6t_unregister_match(&ip6t_nth_reg);
++      printk("ip6t_nth match unloaded\n");
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_owner.c linux-2.4.20/net/ipv6/netfilter/ip6t_owner.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_owner.c   Tue Oct 30 23:08:12 2001
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_owner.c       Wed Sep 24 09:17:52 2003
+@@ -16,6 +16,38 @@
+ MODULE_LICENSE("GPL");
+ static int
++match_comm(const struct sk_buff *skb, const char *comm)
++{
++      struct task_struct *p;
++      struct files_struct *files;
++      int i;
++
++      read_lock(&tasklist_lock);
++      for_each_task(p) {
++              if(strncmp(p->comm, comm, sizeof(p->comm)))
++                      continue;
++
++              task_lock(p);
++              files = p->files;
++              if(files) {
++                      read_lock(&files->file_lock);
++                      for (i=0; i < files->max_fds; i++) {
++                              if (fcheck_files(files, i) == skb->sk->socket->file) {
++                                      read_unlock(&files->file_lock);
++                                      task_unlock(p);
++                                      read_unlock(&tasklist_lock);
++                                      return 1;
++                              }
++                      }
++                      read_unlock(&files->file_lock);
++              }
++              task_unlock(p);
++      }
++      read_unlock(&tasklist_lock);
++      return 0;
++}
++
++static int
+ match_pid(const struct sk_buff *skb, pid_t pid)
+ {
+       struct task_struct *p;
+@@ -119,6 +151,12 @@
+                       return 0;
+       }
++      if(info->match & IP6T_OWNER_COMM) {
++              if (!match_comm(skb, info->comm) ^
++                  !!(info->invert & IP6T_OWNER_COMM))
++                      return 0;
++      }
++
+       return 1;
+ }
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_random.c linux-2.4.20/net/ipv6/netfilter/ip6t_random.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_random.c  Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_random.c      Wed Sep 24 09:17:07 2003
+@@ -0,0 +1,97 @@
++/*
++  This is a module which is used for a "random" match support.
++  This file is distributed under the terms of the GNU General Public
++  License (GPL). Copies of the GPL can be obtained from:
++     ftp://prep.ai.mit.edu/pub/gnu/GPL
++
++  2001-10-14 Fabrice MARIE <fabrice@netfilter.org> : initial implementation.
++  2003-04-30 Maciej Soltysiak <solt@dns.toxicfilms.tv> : IPv6 Port
++*/
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ip.h>
++#include <linux/random.h>
++#include <net/tcp.h>
++#include <linux/spinlock.h>
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_random.h>
++
++MODULE_LICENSE("GPL");
++
++static int
++ip6t_rand_match(const struct sk_buff *pskb,
++             const struct net_device *in,
++             const struct net_device *out,
++             const void *matchinfo,
++             int offset,
++             const void *hdr,
++             u_int16_t datalen,
++             int *hotdrop)
++{
++      /* Parameters from userspace */
++      const struct ip6t_rand_info *info = matchinfo;
++      u_int8_t random_number;
++
++      /* get 1 random number from the kernel random number generation routine */
++      get_random_bytes((void *)(&random_number), 1);
++
++      /* Do we match ? */
++      if (random_number <= info->average)
++              return 1;
++      else
++              return 0;
++}
++
++static int
++ip6t_rand_checkentry(const char *tablename,
++                 const struct ip6t_ip6 *e,
++                 void *matchinfo,
++                 unsigned int matchsize,
++                 unsigned int hook_mask)
++{
++      /* Parameters from userspace */
++      const struct ip6t_rand_info *info = matchinfo;
++
++      if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_rand_info))) {
++              printk("ip6t_random: matchsize %u != %u\n", matchsize,
++                     IP6T_ALIGN(sizeof(struct ip6t_rand_info)));
++              return 0;
++      }
++
++      /* must be  1 <= average % <= 99 */
++      /* 1  x 2.55 = 2   */
++      /* 99 x 2.55 = 252 */
++      if ((info->average < 2) || (info->average > 252)) {
++              printk("ip6t_random:  invalid average %u\n", info->average);
++              return 0;
++      }
++
++      return 1;
++}
++
++static struct ip6t_match ip6t_rand_reg = { 
++      {NULL, NULL},
++      "random",
++      ip6t_rand_match,
++      ip6t_rand_checkentry,
++      NULL,
++      THIS_MODULE };
++
++static int __init init(void)
++{
++      if (ip6t_register_match(&ip6t_rand_reg))
++              return -EINVAL;
++
++      printk("ip6t_random match loaded\n");
++      return 0;
++}
++
++static void __exit fini(void)
++{
++      ip6t_unregister_match(&ip6t_rand_reg);
++      printk("ip6t_random match unloaded\n");
++}
++
++module_init(init);
++module_exit(fini);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6t_rt.c linux-2.4.20/net/ipv6/netfilter/ip6t_rt.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6t_rt.c      Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6t_rt.c  Wed Sep 24 09:16:17 2003
+@@ -0,0 +1,294 @@
++/* Kernel module to match ROUTING parameters. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/ipv6.h>
++#include <linux/types.h>
++#include <net/checksum.h>
++#include <net/ipv6.h>
++
++#include <asm/byteorder.h>
++
++#include <linux/netfilter_ipv6/ip6_tables.h>
++#include <linux/netfilter_ipv6/ip6t_rt.h>
++
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("IPv6 RT match");
++MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
++
++#if 0
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++/* Returns 1 if the id is matched by the range, 0 otherwise */
++static inline int
++segsleft_match(u_int32_t min, u_int32_t max, u_int32_t id, int invert)
++{
++       int r=0;
++       DEBUGP("rt segsleft_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
++              min,id,max);
++       r=(id >= min && id <= max) ^ invert;
++       DEBUGP(" result %s\n",r? "PASS" : "FAILED");
++       return r;
++}
++
++static int
++match(const struct sk_buff *skb,
++      const struct net_device *in,
++      const struct net_device *out,
++      const void *matchinfo,
++      int offset,
++      const void *protohdr,
++      u_int16_t datalen,
++      int *hotdrop)
++{
++       struct ipv6_rt_hdr *route = NULL;
++       const struct ip6t_rt *rtinfo = matchinfo;
++       unsigned int temp;
++       unsigned int len;
++       u8 nexthdr;
++       unsigned int ptr;
++       unsigned int hdrlen = 0;
++       unsigned int ret = 0;
++
++       /* type of the 1st exthdr */
++       nexthdr = skb->nh.ipv6h->nexthdr;
++       /* pointer to the 1st exthdr */
++       ptr = sizeof(struct ipv6hdr);
++       /* available length */
++       len = skb->len - ptr;
++       temp = 0;
++
++        while (ip6t_ext_hdr(nexthdr)) {
++               struct ipv6_opt_hdr *hdr;
++
++              DEBUGP("ipv6_rt header iteration \n");
++
++              /* Is there enough space for the next ext header? */
++                if (len < (int)sizeof(struct ipv6_opt_hdr))
++                        return 0;
++              /* No more exthdr -> evaluate */
++                if (nexthdr == NEXTHDR_NONE) {
++                     break;
++              }
++              /* ESP -> evaluate */
++                if (nexthdr == NEXTHDR_ESP) {
++                     break;
++              }
++
++              hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
++
++              /* Calculate the header length */
++                if (nexthdr == NEXTHDR_FRAGMENT) {
++                        hdrlen = 8;
++                } else if (nexthdr == NEXTHDR_AUTH)
++                        hdrlen = (hdr->hdrlen+2)<<2;
++                else
++                        hdrlen = ipv6_optlen(hdr);
++
++              /* ROUTING -> evaluate */
++                if (nexthdr == NEXTHDR_ROUTING) {
++                     temp |= MASK_ROUTING;
++                     break;
++              }
++
++
++              /* set the flag */
++              switch (nexthdr){
++                     case NEXTHDR_HOP:
++                     case NEXTHDR_ROUTING:
++                     case NEXTHDR_FRAGMENT:
++                     case NEXTHDR_AUTH:
++                     case NEXTHDR_DEST:
++                            break;
++                     default:
++                            DEBUGP("ipv6_rt match: unknown nextheader %u\n",nexthdr);
++                            return 0;
++                            break;
++              }
++
++                nexthdr = hdr->nexthdr;
++                len -= hdrlen;
++                ptr += hdrlen;
++              if ( ptr > skb->len ) {
++                      DEBUGP("ipv6_rt: new pointer is too large! \n");
++                      break;
++              }
++        }
++
++       /* ROUTING header not found */
++       if ( temp != MASK_ROUTING ) return 0;
++
++       if (len < (int)sizeof(struct ipv6_rt_hdr)){
++             *hotdrop = 1;
++                      return 0;
++       }
++
++       if (len < hdrlen){
++             /* Pcket smaller than its length field */
++                      return 0;
++       }
++
++       route = (struct ipv6_rt_hdr *) (skb->data + ptr);
++
++       DEBUGP("IPv6 RT LEN %u %u ", hdrlen, route->hdrlen);
++       DEBUGP("TYPE %04X ", route->type);
++       DEBUGP("SGS_LEFT %u %08X\n", ntohl(route->segments_left), ntohl(route->segments_left));
++
++       DEBUGP("IPv6 RT segsleft %02X ",
++                      (segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1],
++                           ntohl(route->segments_left),
++                           !!(rtinfo->invflags & IP6T_RT_INV_SGS))));
++       DEBUGP("type %02X %02X %02X ",
++                      rtinfo->rt_type, route->type, 
++                      (!(rtinfo->flags & IP6T_RT_TYP) ||
++                           ((rtinfo->rt_type == route->type) ^
++                           !!(rtinfo->invflags & IP6T_RT_INV_TYP))));
++       DEBUGP("len %02X %04X %02X ",
++                      rtinfo->hdrlen, hdrlen,
++                      (!(rtinfo->flags & IP6T_RT_LEN) ||
++                           ((rtinfo->hdrlen == hdrlen) ^
++                           !!(rtinfo->invflags & IP6T_RT_INV_LEN))));
++       DEBUGP("res %02X %02X %02X ", 
++                      (rtinfo->flags & IP6T_RT_RES), ((struct rt0_hdr *)route)->bitmap,
++                      !((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->bitmap)));
++
++       ret = (route != NULL)
++                      &&
++                      (segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1],
++                           ntohl(route->segments_left),
++                           !!(rtinfo->invflags & IP6T_RT_INV_SGS)))
++              &&
++              (!(rtinfo->flags & IP6T_RT_LEN) ||
++                           ((rtinfo->hdrlen == hdrlen) ^
++                           !!(rtinfo->invflags & IP6T_RT_INV_LEN)))
++              &&
++                      (!(rtinfo->flags & IP6T_RT_TYP) ||
++                           ((rtinfo->rt_type == route->type) ^
++                           !!(rtinfo->invflags & IP6T_RT_INV_TYP)))
++              &&
++                      !((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->bitmap));
++
++      DEBUGP("#%d ",rtinfo->addrnr);
++       temp = len = ptr = 0;
++       if ( !(rtinfo->flags & IP6T_RT_FST) ){
++             return ret;
++      } else if (rtinfo->flags & IP6T_RT_FST_NSTRICT) {
++              DEBUGP("Not strict ");
++              if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){
++                      DEBUGP("There isn't enough space\n");
++                      return 0;
++              } else {
++                      DEBUGP("#%d ",rtinfo->addrnr);
++                      ptr = 0;
++                      for(temp=0; temp<(unsigned int)((hdrlen-8)/16); temp++){
++                              len = 0;
++                              while ((u8)(((struct rt0_hdr *)route)->
++                                              addr[temp].s6_addr[len]) ==
++                                      (u8)(rtinfo->addrs[ptr].s6_addr[len])){
++                                      DEBUGP("%02X?%02X ",
++              (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
++                                      (u8)(rtinfo->addrs[ptr].s6_addr[len]));
++                                      len++;
++                                      if ( len == 16 ) break;
++                              }
++                              if (len==16) {
++                                      DEBUGP("ptr=%d temp=%d;\n",ptr,temp);
++                                      ptr++;
++                              } else {
++                                      DEBUGP("%02X?%02X ",
++              (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
++                                      (u8)(rtinfo->addrs[ptr].s6_addr[len]));
++                                      DEBUGP("!ptr=%d temp=%d;\n",ptr,temp);
++                              }
++                              if (ptr==rtinfo->addrnr) break;
++                      }
++                      DEBUGP("ptr=%d len=%d #%d\n",ptr,len, rtinfo->addrnr);
++                      if ( (len == 16) && (ptr == rtinfo->addrnr))
++                              return ret;
++                      else return 0;
++              }
++      } else {
++              DEBUGP("Strict ");
++              if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){
++                      DEBUGP("There isn't enough space\n");
++                      return 0;
++              } else {
++                      DEBUGP("#%d ",rtinfo->addrnr);
++                      for(temp=0; temp<rtinfo->addrnr; temp++){
++                              len = 0;
++                              while ((u8)(((struct rt0_hdr *)route)->
++                                              addr[temp].s6_addr[len]) ==
++                                      (u8)(rtinfo->addrs[temp].s6_addr[len])){
++                                      DEBUGP("%02X?%02X ",
++              (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
++                                      (u8)(rtinfo->addrs[temp].s6_addr[len]));
++                                      len++;
++                                      if ( len == 16 ) break;
++                              }
++                              if (len!=16) {
++                                      DEBUGP("%02X?%02X ",
++              (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
++                                      (u8)(rtinfo->addrs[temp].s6_addr[len]));
++                                      DEBUGP("!len=%d temp=%d;\n",len,temp);
++                                      break;
++                              }
++                      }
++                      DEBUGP("temp=%d len=%d #%d\n",temp,len,rtinfo->addrnr);
++                      if ( (len == 16) && (temp == rtinfo->addrnr) && (temp == (unsigned int)((hdrlen-8)/16)))
++                              return ret;
++                      else return 0;
++              }
++      }
++
++      return 0;
++}
++
++/* Called when user tries to insert an entry of this type. */
++static int
++checkentry(const char *tablename,
++          const struct ip6t_ip6 *ip,
++          void *matchinfo,
++          unsigned int matchinfosize,
++          unsigned int hook_mask)
++{
++       const struct ip6t_rt *rtinfo = matchinfo;
++
++       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_rt))) {
++              DEBUGP("ip6t_rt: matchsize %u != %u\n",
++                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_rt)));
++              return 0;
++       }
++       if (rtinfo->invflags & ~IP6T_RT_INV_MASK) {
++              DEBUGP("ip6t_rt: unknown flags %X\n",
++                      rtinfo->invflags);
++              return 0;
++       }
++       if ( (rtinfo->flags & (IP6T_RT_RES|IP6T_RT_FST_MASK)) && 
++                     (!(rtinfo->flags & IP6T_RT_TYP) || 
++                     (rtinfo->rt_type != 0) || 
++                     (rtinfo->invflags & IP6T_RT_INV_TYP)) ) {
++            DEBUGP("`--rt-type 0' required before `--rt-0-*'");
++              return 0;
++       }
++
++       return 1;
++}
++
++static struct ip6t_match rt_match
++= { { NULL, NULL }, "rt", &match, &checkentry, NULL, THIS_MODULE };
++
++static int __init init(void)
++{
++       return ip6t_register_match(&rt_match);
++}
++
++static void __exit cleanup(void)
++{
++       ip6t_unregister_match(&rt_match);
++}
++
++module_init(init);
++module_exit(cleanup);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6table_mangle.c linux-2.4.20/net/ipv6/netfilter/ip6table_mangle.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6table_mangle.c      Mon Feb 25 19:38:14 2002
++++ linux-2.4.20/net/ipv6/netfilter/ip6table_mangle.c  Wed Sep 24 09:16:14 2003
+@@ -157,7 +157,7 @@
+       hop_limit = (*pskb)->nh.ipv6h->hop_limit;
+       /* flowlabel and prio (includes version, which shouldn't change either */
+-      flowlabel = (u_int32_t) (*pskb)->nh.ipv6h;
++      flowlabel = *((u_int32_t *) (*pskb)->nh.ipv6h);
+       ret = ip6t_do_table(pskb, hook, in, out, &packet_mangler, NULL);
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/ipv6/netfilter/ip6table_raw.c linux-2.4.20/net/ipv6/netfilter/ip6table_raw.c
+--- linux-2.4.20.org/net/ipv6/netfilter/ip6table_raw.c Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/net/ipv6/netfilter/ip6table_raw.c     Wed Sep 24 09:18:14 2003
+@@ -0,0 +1,156 @@
++/*
++ * IPv6 raw table, a port of the IPv4 raw table to IPv6
++ *
++ * Copyright (C) 2003 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
++ */
++#include <linux/module.h>
++#include <linux/netfilter_ipv6/ip6_tables.h>
++
++#define RAW_VALID_HOOKS ((1 << NF_IP6_PRE_ROUTING) | (1 << NF_IP6_LOCAL_OUT))
++
++#if 0
++#define DEBUGP(x, args...)    printk(KERN_DEBUG x, ## args)
++#else
++#define DEBUGP(x, args...)
++#endif
++
++/* Standard entry. */
++struct ip6t_standard
++{
++      struct ip6t_entry entry;
++      struct ip6t_standard_target target;
++};
++
++struct ip6t_error_target
++{
++      struct ip6t_entry_target target;
++      char errorname[IP6T_FUNCTION_MAXNAMELEN];
++};
++
++struct ip6t_error
++{
++      struct ip6t_entry entry;
++      struct ip6t_error_target target;
++};
++
++static struct
++{
++      struct ip6t_replace repl;
++      struct ip6t_standard entries[2];
++      struct ip6t_error term;
++} initial_table __initdata 
++= { { "raw", RAW_VALID_HOOKS, 3,
++      sizeof(struct ip6t_standard) * 2 + sizeof(struct ip6t_error),
++      { [NF_IP6_PRE_ROUTING]  0,
++      [NF_IP6_LOCAL_OUT]      sizeof(struct ip6t_standard) },
++      { [NF_IP6_PRE_ROUTING]  0,
++      [NF_IP6_LOCAL_OUT]      sizeof(struct ip6t_standard) },
++      0, NULL, { } },
++    {
++          /* PRE_ROUTING */
++            { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 },
++              0,
++              sizeof(struct ip6t_entry),
++              sizeof(struct ip6t_standard),
++              0, { 0, 0 }, { } },
++            { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } },
++              -NF_ACCEPT - 1 } },
++          /* LOCAL_OUT */
++            { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 },
++              0,
++              sizeof(struct ip6t_entry),
++              sizeof(struct ip6t_standard),
++              0, { 0, 0 }, { } },
++            { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } },
++              -NF_ACCEPT - 1 } },
++    },
++    /* ERROR */
++    { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 },
++      0,
++      sizeof(struct ip6t_entry),
++      sizeof(struct ip6t_error),
++      0, { 0, 0 }, { } },
++      { { { { IP6T_ALIGN(sizeof(struct ip6t_error_target)), IP6T_ERROR_TARGET } },
++        { } },
++      "ERROR"
++      }
++    }
++};
++
++static struct ip6t_table packet_raw = { 
++      .list = { NULL, NULL }, 
++      .name = "raw", 
++      .table = &initial_table.repl,
++      .valid_hooks = RAW_VALID_HOOKS, 
++      .lock = RW_LOCK_UNLOCKED, 
++      .private = NULL, 
++      .me = THIS_MODULE
++};
++
++/* The work comes in here from netfilter.c. */
++static unsigned int
++ip6t_hook(unsigned int hook,
++       struct sk_buff **pskb,
++       const struct net_device *in,
++       const struct net_device *out,
++       int (*okfn)(struct sk_buff *))
++{
++      return ip6t_do_table(pskb, hook, in, out, &packet_raw, NULL);
++}
++
++static struct nf_hook_ops ip6t_ops[] = { 
++      /* PRE_ROUTING */
++      { .list = { NULL, NULL }, 
++        .hook = ip6t_hook, 
++        .pf = PF_INET6,
++        .hooknum = NF_IP6_PRE_ROUTING,
++        .priority = NF_IP6_PRI_FIRST },
++      /* LOCAL_OUT */
++      { .list = { NULL, NULL },
++        .hook = ip6t_hook, 
++        .pf = PF_INET6, 
++        .hooknum = NF_IP6_LOCAL_OUT,
++        .priority = NF_IP6_PRI_FIRST }
++};
++
++static int __init init(void)
++{
++      int ret;
++
++      /* Register table */
++      ret = ip6t_register_table(&packet_raw);
++      if (ret < 0)
++              return ret;
++
++      /* Register hooks */
++      ret = nf_register_hook(&ip6t_ops[0]);
++      if (ret < 0)
++              goto cleanup_table;
++
++      ret = nf_register_hook(&ip6t_ops[1]);
++      if (ret < 0)
++              goto cleanup_hook0;
++
++      return ret;
++
++ cleanup_hook0:
++      nf_unregister_hook(&ip6t_ops[0]);
++ cleanup_table:
++      ip6t_unregister_table(&packet_raw);
++
++      return ret;
++}
++
++static void __exit fini(void)
++{
++      unsigned int i;
++
++      for (i = 0; i < sizeof(ip6t_ops)/sizeof(struct nf_hook_ops); i++)
++              nf_unregister_hook(&ip6t_ops[i]);
++
++      ip6t_unregister_table(&packet_raw);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+diff -Nur --exclude '*.orig' linux-2.4.20.org/net/netsyms.c linux-2.4.20/net/netsyms.c
+--- linux-2.4.20.org/net/netsyms.c     Wed Sep 24 08:52:55 2003
++++ linux-2.4.20/net/netsyms.c Wed Sep 24 09:26:45 2003
+@@ -296,6 +296,9 @@
+ EXPORT_SYMBOL(unregister_inet6addr_notifier);
+ #include <net/ip6_route.h>
+ EXPORT_SYMBOL(ip6_route_output);
++#ifdef CONFIG_NETFILTER
++EXPORT_SYMBOL(ip6_route_me_harder);
++#endif
+ #endif
+ #if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
+ /* inet functions common to v4 and v6 */
+@@ -571,6 +574,7 @@
+ #endif
+ #ifdef CONFIG_NETFILTER
+ #include <linux/netfilter.h>
++#include <linux/netfilter_logging.h>
+ EXPORT_SYMBOL(nf_register_hook);
+ EXPORT_SYMBOL(nf_unregister_hook);
+ EXPORT_SYMBOL(nf_register_sockopt);
+@@ -583,6 +587,10 @@
+ EXPORT_SYMBOL(nf_setsockopt);
+ EXPORT_SYMBOL(nf_getsockopt);
+ EXPORT_SYMBOL(ip_ct_attach);
++EXPORT_SYMBOL(nf_log_register);
++EXPORT_SYMBOL(nf_log_unregister);
++EXPORT_SYMBOL(nf_log_packet);
++EXPORT_SYMBOL(nf_log);
+ #ifdef CONFIG_INET
+ #include <linux/netfilter_ipv4.h>
+ EXPORT_SYMBOL(ip_route_me_harder);
+@@ -593,13 +601,6 @@
+ EXPORT_SYMBOL(softnet_data);
+-#if defined(CONFIG_IP_NF_MATCH_STEALTH_MODULE)
+-#if !defined (CONFIG_IPV6_MODULE) && !defined (CONFIG_KHTTPD) && !defined (CONFIG_KHTTPD_MODULE)
+-EXPORT_SYMBOL(tcp_v4_lookup_listener);
+-#endif
+-extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif);
+-EXPORT_SYMBOL(udp_v4_lookup);
+-#endif
+ #if defined(CONFIG_GRKERNSEC_RANDID)
+ EXPORT_SYMBOL(ip_randomid);
+@@ -641,4 +642,28 @@
+ EXPORT_SYMBOL(wireless_send_event);
+ #endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
++#if defined(CONFIG_IP_NF_MATCH_STEALTH_MODULE)
++#if !defined (CONFIG_IPV6_MODULE) && !defined (CONFIG_KHTTPD) && !defined (CONFIG_KHTTPD_MODULE)
++EXPORT_SYMBOL(tcp_v4_lookup_listener);
++#endif
++#endif
++
++#if defined(CONFIG_IP_NF_MATCH_STEALTH_MODULE)
++extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif);
++#endif
++
++#if defined(CONFIG_IP_NF_MATCH_STEALTH_MODULE)||defined(CONFIG_IP_NF_MATCH_OWNER)||defined(CONFIG_IP_NF_MATCH_OWNER_MODULE)
++EXPORT_SYMBOL(udp_v4_lookup);
++#endif
++
++#if defined(CONFIG_IP_NF_MATCH_OWNER)||defined(CONFIG_IP_NF_MATCH_OWNER_MODULE)
++EXPORT_SYMBOL(tcp_v4_lookup);
++#endif /* CONFIG_IP_NF_MATCH_OWNER */
++
++#if defined(CONFIG_IP_NF_MATCH_OWNER)||defined(CONFIG_IP_NF_MATCH_OWNER_MODULE)
++#if !(defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE))
++EXPORT_SYMBOL(tcp_timewait_cachep);
++#endif
++#endif /* CONFIG_IP_NF_MATCH_OWNER */
++
+ #endif  /* CONFIG_NET */
+diff -Nur --exclude '*.orig' linux-2.4.20.org/netfilter-patch-o-matic/patches linux-2.4.20/netfilter-patch-o-matic/patches
+--- linux-2.4.20.org/netfilter-patch-o-matic/patches   Thu Jan  1 00:00:00 1970
++++ linux-2.4.20/netfilter-patch-o-matic/patches       Wed Sep 24 09:20:32 2003
+@@ -0,0 +1,84 @@
++./submitted/01_2.4.19.patch
++./submitted/02_2.4.20.patch
++./submitted/03_2.4.21.patch
++./submitted/04_2.4.22.patch
++./submitted/44_backport_ah_esp_fixes.patch
++./submitted/45_masq_routing_check.patch
++./submitted/54_ip_nat-macro-args.patch
++./submitted/58-ip_conntrack-macro-args.patch
++./submitted/60_nat_tftp-remove-warning.patch
++./submitted/72_recent_procfs_fix.patch
++./submitted/73_ipt_MASQUERADE-oif.patch
++./submitted/74_nat-range-fix.patch
++./submitted/75_REJECT_localpmtu-fix.patch
++./submitted/76_snmp-checksum_h-fix.patch
++./pending/39_ip_conntrack-proc.patch
++./pending/40_nf-log.patch
++./pending/40_nf-log.patch.ipv6
++./pending/55_ipt_unclean-tcp-flag-table.patch
++./pending/59_ip_nat_h-unused-var.patch
++./pending/61-remove-memsets.patch
++./pending/63_getorigdst-tuple-zero.patch
++./pending/64_masquerade-sameip-noflush.patch
++./pending/65_irc-conntrack-mirc-serverlookup.patch
++./pending/66_ipv6header.patch
++./pending/67_nolocalout.patch
++./pending/68_local-nullbinding.patch
++./base/HL.patch.ipv6
++./base/IPV4OPTSSTRIP.patch
++./base/NETLINK.patch
++./base/NETMAP.patch
++./base/REJECT.patch.ipv6
++./base/SAME.patch
++./base/TTL.patch
++./base/connlimit.patch
++./base/fuzzy.patch
++./base/fuzzy6.patch.ipv6
++./base/iprange.patch
++./base/ipv4options.patch
++./base/mport.patch
++./base/nth.patch
++./base/nth6.patch.ipv6
++./base/osf.patch
++./base/pool.patch
++./base/psd.patch
++./base/quota.patch
++./base/random.patch
++./base/random6.patch.ipv6
++./base/realm.patch
++./base/time.patch
++./base/u32.patch
++./extra/CLASSIFY.patch
++./extra/CONNMARK.patch
++./extra/IMQ.patch
++./extra/IMQ.patch.ipv6
++./extra/IPMARK.patch
++./extra/ROUTE.patch
++./extra/ROUTE.patch.ipv6
++./extra/TCPLAG.patch
++./extra/XOR.patch
++./extra/addrtype.patch
++./extra/condition.patch
++./extra/condition6.patch.ipv6
++./extra/cuseeme-nat.patch
++./extra/eggdrop-conntrack.patch
++./extra/h323-conntrack-nat.patch
++./extra/ipt_TARPIT.patch
++./extra/iptables-loopcheck-speedup.patch
++./extra/mms-conntrack-nat.patch
++./extra/netfilter-docbook.patch
++./extra/nfnetlink-ctnetlink-0.11.patch
++./extra/owner-socketlookup.patch
++./extra/ownercmd.patch.ipv6
++./extra/pptp-conntrack-nat.patch
++./extra/quake3-conntrack.patch
++./extra/rpc.patch
++./extra/rsh.patch
++./extra/string.patch
++./extra/talk-conntrack-nat.patch
++./userspace/ipt_REJECT-fake-source.patch
++./userspace/mark-bitwise-ops.patch
++./userspace/raw.patch
++./userspace/raw.patch.ipv6
++./optimizations/ip_ct_refresh_optimization.patch
++./optimizations/ip_ct_refresh_optimization_pptp.patch
This page took 1.079522 seconds and 4 git commands to generate.