diff -u --new-file --recursive linux-2.4.18_vanilla/Documentation/Configure.help linux-2.4.18_iptables-1.2.7-20020511/Documentation/Configure.help --- linux-2.4.18_vanilla/Documentation/Configure.help 2002-02-25 21:37:51.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/Documentation/Configure.help 2002-07-07 00:20:47.000000000 +0200 @@ -2368,6 +2368,40 @@ If you want to compile it as a module, say 'M' here and read Documentation/modules.txt. If unsure, say 'N'. +Eggdrop bot support +CONFIG_IP_NF_EGG + If you are running an eggdrop hub bot on this machine, then you + may want to enable this feature. This enables eggdrop bots to share + their user file to other eggdrop bots. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +H.323 (netmeeting) support +CONFIG_IP_NF_H323 + H.323 is a standard signalling protocol used by teleconferencing + softwares like netmeeting. With the ip_conntrack_h323 and + the ip_nat_h323 modules you can support the protocol on a connection + tracking/NATing firewall. + + If you want to compile it as a module, say 'M' here and read + Documentation/modules.txt. If unsure, say 'N'. + +Talk protocol support +CONFIG_IP_NF_TALK + The talk protocols (both otalk/talk - or talk/ntalk, to confuse + you by the different namings about which is old or which is new :-) + use an additional channel to setup the talk session and a separated + data channel for the actual conversation (like in FTP). Both the + initiating and the setup channels are over UDP, while the data channel + is over TCP, on a random port. The conntrack part of this extension + will enable you to let in/out talk sessions easily by matching these + connections as RELATED by the state match, while the NAT part helps + you to let talk sessions trough a NAT machine. + + If you want to compile it as a module, say 'M' here and read + Documentation/modules.txt. If unsure, say 'N'. + FTP protocol support CONFIG_IP_NF_FTP Tracking FTP connections is problematic: special helpers are @@ -2395,6 +2429,14 @@ If you want to compile it as a module, say M here and read . 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'. + + limit match support CONFIG_IP_NF_MATCH_LIMIT limit matching allows you to control the rate at which a rule can be @@ -2404,6 +2446,17 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +skb->pkt_type packet match support (EXPERIMENTAL) +CONFIG_IP_NF_MATCH_PKTTYPE + This patch allows you to match packet in accrodance + to its "class", eg. BROADCAST, MULTICAST, ... + + Typical usage: + iptables -A INPUT -m pkttype --pkt-type broadcast -j LOG + + 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_IP_NF_MATCH_MAC MAC matching allows you to match packets based on the source @@ -2421,6 +2474,14 @@ If you want to compile it as a module, say M here and read . 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 @@ -2454,6 +2515,108 @@ If you want to compile it as a module, say M here and read Documentation/modules.txt. If unsure, say `N'. +DSCP match support +CONFIG_IP_NF_TARGET_DSCP + This option adds a `DSCP' match, which allows you to match against + the IPv4 header DSCP field (DSCP codepoint). + + The DSCP codepoint can have any value between 0x0 and 0x4f. + + If you want to compile it as a module, say M here and read + 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'. + + +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'. + + +Nth 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'. + + +recent match support +CONFIG_IP_NF_MATCH_RECENT + This option allows you to create lists of recently seen source + addresses and then match against that list based on number of + times seen and the time since last seen. An option is also + availible to require a match against TTL in addition to source + address. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + TOS match support CONFIG_IP_NF_MATCH_TOS TOS matching allows you to match packets based on the Type Of @@ -2462,6 +2625,31 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +conntrack match support +CONFIG_IP_NF_MATCH_CONNTRACK + This is a general conntrack match module, a superset of the state match. + + It allows matching on additional conntrack information, which is + useful in complex configurations, such as NAT gateways with multiple + internet links or tunnels. + + 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 supplies two modules, + ip_conntrack_rpc_udp and ip_conntrack_rpc_tcp, which track + portmapper requests using UDP and TCP respectively. It also adds + the record_rpc match for iptables, which matches if the source of + the packet has requested that port through the portmapper before, + or it is a new GET request to the portmapper, allowing effective RPC + filtering. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + Connection state match support CONFIG_IP_NF_MATCH_STATE Connection state matching allows you to match packets based on their @@ -2471,6 +2659,27 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +String match support (EXPERIMENTAL) +CONFIG_IP_NF_MATCH_STRING + String matching alows you to match packets which contain a + specified string of characters. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +Bytelimit match support (EXPERIMENTAL) +CONFIG_IP_NF_MATCH_BYTELIMIT + Bytelimit allows to shape network traffic easily, to control + bandwidth allocated to a specific service or a specific host, + for instance. In many situations, it can save you the hassle + of understanding "classic" traffic control (which uses qdisc, + and many other things you don't want to know about, when your + only goal is to prevent a specific host to exceed a given + bandwidth). + + 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 @@ -2496,6 +2705,24 @@ If you want to compile it as a module, say M here and read . 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 @@ -2513,6 +2740,19 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +Local NAT support +CONFIG_IP_NF_NAT_LOCAL + This option enables support for NAT of locally originated connections. + Enable this if you need to use destination NAT on connections + originating from local processes on the nat box itself. + + Please note that you will need a recent version (>= 1.2.6a) + of the iptables userspace program in order to use this feature. + See http://www.iptables.org/ for download instructions. + + If unsure, say 'N'. + + Full NAT (Network Address Translation) CONFIG_IP_NF_NAT The Full NAT option allows masquerading, port forwarding and other @@ -2547,6 +2787,27 @@ If you want to compile it as a module, say M here and read . 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 @@ -2566,6 +2827,21 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +DSCP target support +CONFIG_IP_NF_TARGET_DSCP + This option adds a `DSCP' target, which allows you to create rules in + the iptables mangle table. The selected packet has the DSCP field set + to the hex value provided on the command line; unlike the TOS target + which will only set the legal values within ip.h. + + The DSCP field can be set to any value between 0x0 and 0x4f. It does + take into account that bits 6 and 7 are used by ECN. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + + + TOS target support CONFIG_IP_NF_TARGET_TOS This option adds a `TOS' target, which allows you to create rules in @@ -2612,6 +2888,14 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +Helper match support +CONFIG_IP_NF_MATCH_HELPER + Helper matching allows you to match packets in dynamic connections + tracked by a conntrack-helper, ie. ip_conntrack_ftp + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `Y'. + TCPMSS match support CONFIG_IP_NF_MATCH_TCPMSS This option adds a `tcpmss' match, which allows you to examine the @@ -2634,6 +2918,38 @@ 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. + LOG target support CONFIG_IP_NF_TARGET_LOG This option adds a `LOG' target, which allows you to create rules in @@ -2664,6 +2980,60 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +EUI64 address check (EXPERIMENTAL) +CONFIG_IP6_NF_MATCH_EUI64 + This module performs checking on the IPv6 source address + Compares the last 64 bits with the EUI64 (delivered + from the MAC address) address + + 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'. + + MAC address match support CONFIG_IP6_NF_MATCH_MAC mac matching allows you to match packets based on the source @@ -2672,6 +3042,14 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +length match support +CONFIG_IP6_NF_MATCH_LENGTH + This option allows you to match the length of a packet against a + specific value or range of values. + + 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 @@ -2715,6 +3093,15 @@ If you want to compile it as a module, say M here and read . 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'. + Packet filtering CONFIG_IP6_NF_FILTER Packet filtering defines a table `filter', which has a series of diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_arp.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_arp.h --- linux-2.4.18_vanilla/include/linux/netfilter_arp.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_arp.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,19 @@ +#ifndef __LINUX_ARP_NETFILTER_H +#define __LINUX_ARP_NETFILTER_H + +/* ARP-specific defines for netfilter. + * (C)2002 Rusty Russell IBM -- This code is GPL. + */ + +#include +#include + +/* There is no PF_ARP. */ +#define NF_ARP 0 + +/* ARP Hooks */ +/* Coming in. */ +#define NF_ARP_IN 0 +#define NF_ARP_OUT 1 + +#endif /* __LINUX_ARP_NETFILTER_H */ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack.h 2002-02-25 21:38:13.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack.h 2002-07-07 00:19:39.000000000 +0200 @@ -6,6 +6,7 @@ #include #include +#include enum ip_conntrack_info { @@ -62,30 +63,63 @@ #define IP_NF_ASSERT(x) #endif +#ifdef CONFIG_IP_NF_NAT_NEEDED +#include +#endif + +/* Add protocol helper include file here */ +#include +#include + +#include +#include + struct ip_conntrack_expect { - /* Internal linked list */ + /* Internal linked list (global expectation list) */ struct list_head list; + /* expectation list for this master */ + struct list_head expected_list; + + /* The conntrack of the master connection */ + struct ip_conntrack *expectant; + + /* The conntrack of the sibling connection, set after + * expectation arrived */ + struct ip_conntrack *sibling; + + /* Tuple saved for conntrack */ + struct ip_conntrack_tuple ct_tuple; + + /* Timer function; deletes the expectation. */ + struct timer_list timeout; + + /* Data filled out by the conntrack helpers follow: */ + /* We expect this tuple, with the following mask */ struct ip_conntrack_tuple tuple, mask; /* Function to call after setup and insertion */ int (*expectfn)(struct ip_conntrack *new); - /* The conntrack we are part of (set iff we're live) */ - struct ip_conntrack *expectant; -}; - + /* At which sequence number did this expectation occur */ + u_int32_t seq; + + union { + /* insert conntrack helper private data (expect) here */ + struct ip_ct_talk_expect exp_talk_info; + struct ip_ct_h225_expect exp_h225_info; + struct ip_ct_ftp_expect exp_ftp_info; + struct ip_ct_irc_expect exp_irc_info; + #ifdef CONFIG_IP_NF_NAT_NEEDED -#include -#endif - -#include - -#if defined(CONFIG_IP_NF_IRC) || defined(CONFIG_IP_NF_IRC_MODULE) -#include + union { + /* insert nat helper private data (expect) here */ + } nat; #endif + } help; +}; struct ip_conntrack { @@ -104,10 +138,13 @@ /* If we're expecting another related connection, this will be in expected linked list */ - struct ip_conntrack_expect expected; + struct list_head sibling_list; + + /* Current number of expected connections */ + unsigned int expecting; - /* If we were expected by another connection, this will be it */ - struct nf_ct_info master; + /* If we were expected by an expectation, this will be it */ + struct ip_conntrack_expect *master; /* Helper, if any. */ struct ip_conntrack_helper *helper; @@ -124,10 +161,11 @@ } proto; union { - struct ip_ct_ftp ct_ftp_info; -#if defined(CONFIG_IP_NF_IRC) || defined(CONFIG_IP_NF_IRC_MODULE) - struct ip_ct_irc ct_irc_info; -#endif + /* insert conntrack helper private data (master) here */ + struct ip_ct_talk_master ct_talk_info; + struct ip_ct_h225_master ct_h225_info; + struct ip_ct_ftp_master ct_ftp_info; + struct ip_ct_irc_master ct_irc_info; } help; #ifdef CONFIG_IP_NF_NAT_NEEDED @@ -145,6 +183,9 @@ }; +/* get master conntrack via master expectation */ +#define master_ct(conntr) (conntr->master ? conntr->master->expectant : NULL) + /* Alter reply tuple (maybe alter helper). If it's already taken, return 0 and don't do alteration. */ extern int diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_core.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_core.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_core.h 2001-04-27 23:15:01.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_core.h 2002-07-07 00:19:39.000000000 +0200 @@ -15,7 +15,7 @@ extern void ip_conntrack_cleanup(void); struct ip_conntrack_protocol; -extern struct ip_conntrack_protocol *find_proto(u_int8_t protocol); +extern struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol); /* Like above, but you already have conntrack read lock. */ extern struct ip_conntrack_protocol *__find_proto(u_int8_t protocol); extern struct list_head protocol_list; diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_ftp.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_ftp.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_ftp.h 2001-04-26 00:00:28.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_ftp.h 2002-07-07 00:19:39.000000000 +0200 @@ -11,6 +11,8 @@ /* Protects ftp part of conntracks */ DECLARE_LOCK_EXTERN(ip_ftp_lock); +#define FTP_PORT 21 + enum ip_ct_ftp_type { /* PORT command from client */ @@ -23,18 +25,20 @@ IP_CT_FTP_EPSV, }; -/* We record seq number and length of ftp ip/port text here: all in - host order. */ -struct ip_ct_ftp +/* This structure is per expected connection */ +struct ip_ct_ftp_expect { - /* This tells NAT that this is an ftp connection */ - int is_ftp; - u_int32_t seq; - /* 0 means not found yet */ - u_int32_t len; - enum ip_ct_ftp_type ftptype; - /* Port that was to be used */ - u_int16_t port; + /* We record seq number and length of ftp ip/port text here: all in + * host order. */ + + /* sequence number of IP address in packet is in ip_conntrack_expect */ + u_int32_t len; /* length of IP address */ + enum ip_ct_ftp_type ftptype; /* PORT or PASV ? */ + u_int16_t port; /* TCP port that was to be used */ +}; + +/* This structure exists only once per master */ +struct ip_ct_ftp_master { /* Next valid seq position for cmd matching after newline */ u_int32_t seq_aft_nl[IP_CT_DIR_MAX]; /* 0 means seq_match_aft_nl not set */ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_h323.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_h323.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_h323.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_h323.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,32 @@ +#ifndef _IP_CONNTRACK_H323_H +#define _IP_CONNTRACK_H323_H +/* H.323 connection tracking. */ + +#ifndef __KERNEL__ +#error Only in kernel. +#endif + +/* Protects H.323 related data */ +DECLARE_LOCK_EXTERN(ip_h323_lock); + +/* Default H.225 port */ +#define H225_PORT 1720 + +/* This structure is per expected connection */ +struct ip_ct_h225_expect { + u_int16_t port; /* Port of the H.225 helper/RTCP/RTP channel */ + enum ip_conntrack_dir dir; /* Direction of the original connection */ + unsigned int offset; /* offset of the address in the payload */ +}; + +/* This structure exists only once per master */ +struct ip_ct_h225_master { + int is_h225; /* H.225 or H.245 connection */ +#ifdef CONFIG_IP_NF_NAT_NEEDED + enum ip_conntrack_dir dir; /* Direction of the original connection */ + u_int32_t seq[IP_CT_DIR_MAX]; /* Exceptional packet mangling for signal addressess... */ + unsigned int offset[IP_CT_DIR_MAX]; /* ...and the offset of the addresses in the payload */ +#endif +}; + +#endif /* _IP_CONNTRACK_H323_H */ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_helper.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_helper.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_helper.h 2000-12-11 23:31:23.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_helper.h 2002-07-07 00:19:39.000000000 +0200 @@ -5,10 +5,19 @@ struct module; +/* Reuse expectation when max_expected reached */ +#define IP_CT_HELPER_F_REUSE_EXPECT 0x01 + struct ip_conntrack_helper { - /* Internal use. */ - struct list_head list; + struct list_head list; /* Internal use. */ + + const char *name; /* name of the module */ + unsigned char flags; /* Flags (see above) */ + struct module *me; /* pointer to self */ + unsigned int max_expected; /* Maximum number of concurrent + * expected connections */ + unsigned int timeout; /* timeout for expecteds */ /* Mask of things we will help (compared against server response) */ struct ip_conntrack_tuple tuple; @@ -24,11 +33,13 @@ extern int ip_conntrack_helper_register(struct ip_conntrack_helper *); extern void ip_conntrack_helper_unregister(struct ip_conntrack_helper *); -/* Add an expected connection: can only have one per connection */ +extern struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple); + +/* Add an expected connection: can have more than one per connection */ extern int ip_conntrack_expect_related(struct ip_conntrack *related_to, - const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *mask, - int (*expectfn)(struct ip_conntrack *)); -extern void ip_conntrack_unexpect_related(struct ip_conntrack *related_to); + struct ip_conntrack_expect *exp); +extern int ip_conntrack_change_expect(struct ip_conntrack_expect *expect, + struct ip_conntrack_tuple *newtuple); +extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp); #endif /*_IP_CONNTRACK_HELPER_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_irc.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_irc.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_irc.h 2001-10-31 01:08:12.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_irc.h 2002-07-07 00:19:39.000000000 +0200 @@ -20,7 +20,7 @@ #include -#define IP_CONNTR_IRC 2 +#define IRC_PORT 6667 struct dccproto { char* match; @@ -32,16 +32,18 @@ /* We record seq number and length of irc ip/port text here: all in host order. */ -struct ip_ct_irc + +/* This structure is per expected connection */ +struct ip_ct_irc_expect { - /* This tells NAT that this is an IRC connection */ - int is_irc; - /* sequence number where address part of DCC command begins */ - u_int32_t seq; - /* 0 means not found yet */ + /* length of IP address */ u_int32_t len; /* Port that was to be used */ u_int16_t port; }; +/* This structure exists only once per master */ +struct ip_ct_irc_master { +}; + #endif /* _IP_CONNTRACK_IRC_H */ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_protocol.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_protocol.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_protocol.h 2001-04-27 23:15:01.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_protocol.h 2002-07-07 00:19:39.000000000 +0200 @@ -42,6 +42,13 @@ int (*new)(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len); + /* Called when a conntrack entry is destroyed */ + void (*destroy)(struct ip_conntrack *conntrack); + + /* Has to decide if a expectation matches one packet or not */ + int (*exp_matches_pkt)(struct ip_conntrack_expect *exp, + struct sk_buff **pskb); + /* Module (if any) which this is connected to. */ struct module *me; }; diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_rpc.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_rpc.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_rpc.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_rpc.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,71 @@ +/* RPC extension for IP connection tracking. */ +/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */ + +#include +#include +#include +#include +#include + +#include +#include + +/* Data in RPC packet is encoded in XDR */ +#define IXDR_GET_INT32(buf) ((u_int32_t) ntohl((uint32_t)*buf)) + +#if 0 +#define DEBUGRPC printk +#else +#define DEBUGRPC(format, args...) +#endif + +/* Fast timeout, to deny DoS atacks */ +#define EXP (60 * HZ) + +/* Normal timeouts */ +#define EXPIRES (180 * HZ) + + /* For future conections RPC, using client's cache bindings */ + /* I'll use ip_conntrack_lock to lock these lists */ + +/* This identifies each request and stores protocol */ + +struct request_p { + struct list_head list; + + u_int32_t xid; + u_int32_t ip; + u_int16_t port; + + /* Protocol */ + u_int16_t proto; + + struct timer_list timeout; +}; + +struct expect_rpc { + /* list */ + struct list_head list; + + /* binding */ + u_int32_t ip; + u_int16_t port; + u_int32_t ip_rmt; + u_int16_t proto; + + /* Expect RPC conection for long time, but this time expires */ + struct timer_list timeout; +}; + +inline int expect_rpc_cmp(const struct expect_rpc *l, u_int16_t port, + u_int32_t ip, u_int32_t ip_rmt, u_int16_t proto) +{ + return (l->ip == ip && l->port == port && + l->ip_rmt == ip_rmt && l->proto == proto); +} + +inline int request_p_cmp(const struct request_p *p, u_int32_t xid, + u_int32_t ip, u_int32_t port) { + return(p->xid == xid && p->ip == ip && p->port); + +} diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_talk.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_talk.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_talk.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_talk.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,153 @@ +#ifndef _IP_CONNTRACK_TALK_H +#define _IP_CONNTRACK_TALK_H +/* TALK tracking. */ + +#ifndef __KERNEL__ +#error Only in kernel. +#endif + +#include +#include + +/* Protects talk part of conntracks */ +DECLARE_LOCK_EXTERN(ip_talk_lock); + +#define TALK_PORT 517 +#define NTALK_PORT 518 + +/* talk structures and constants from */ + +/* + * 4.3BSD struct sockaddr + */ +struct talk_addr { + u_int16_t ta_family; + u_int16_t ta_port; + u_int32_t ta_addr; + u_int32_t ta_junk1; + u_int32_t ta_junk2; +}; + +#define TALK_OLD_NSIZE 9 +#define TALK_NSIZE 12 +#define TALK_TTY_NSIZE 16 + +/* + * Client->server request message formats. + */ +struct talk_msg { + u_char type; /* request type, see below */ + char l_name[TALK_OLD_NSIZE];/* caller's name */ + char r_name[TALK_OLD_NSIZE];/* callee's name */ + u_char pad; + u_int32_t id_num; /* message id */ + int32_t pid; /* caller's process id */ + char r_tty[TALK_TTY_NSIZE];/* callee's tty name */ + struct talk_addr addr; /* old (4.3) style */ + struct talk_addr ctl_addr; /* old (4.3) style */ +}; + +struct ntalk_msg { + u_char vers; /* protocol version */ + u_char type; /* request type, see below */ + u_char answer; /* not used */ + u_char pad; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* old (4.3) style */ + struct talk_addr ctl_addr; /* old (4.3) style */ + int32_t pid; /* caller's process id */ + char l_name[TALK_NSIZE];/* caller's name */ + char r_name[TALK_NSIZE];/* callee's name */ + char r_tty[TALK_TTY_NSIZE];/* callee's tty name */ +}; + +struct ntalk2_msg { + u_char vers; /* talk protocol version */ + u_char type; /* request type */ + u_char answer; /* */ + u_char extended; /* !0 if additional parts */ + u_int32_t id_num; /* message id number (dels) */ + struct talk_addr addr; /* target address */ + struct talk_addr ctl_addr; /* reply to address */ + int32_t pid; /* caller's process id */ + char l_name[TALK_NSIZE]; /* caller's name */ + char r_name[TALK_NSIZE]; /* callee's name */ + char r_tty[TALK_TTY_NSIZE]; /* callee's tty */ +}; + +/* + * Server->client response message formats. + */ +struct talk_response { + u_char type; /* type of request message, see below */ + u_char answer; /* response to request message, see below */ + u_char pad[2]; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* address for establishing conversation */ +}; + +struct ntalk_response { + u_char vers; /* protocol version */ + u_char type; /* type of request message, see below */ + u_char answer; /* response to request message, see below */ + u_char pad; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* address for establishing conversation */ +}; + +struct ntalk2_response { + u_char vers; /* protocol version */ + u_char type; /* type of request message */ + u_char answer; /* response to request */ + u_char rvers; /* Version of answering vers*/ + u_int32_t id_num; /* message id number */ + struct talk_addr addr; /* address for connection */ + /* This is at the end to compatiblize this with NTALK version. */ + char r_name[TALK_NSIZE]; /* callee's name */ +}; + +#define TALK_STR(data, talk_str, member) ((struct talk_str *)data)->member) +#define TALK_RESP(data, ver, member) (ver ? ((struct ntalk_response *)data)->member : ((struct talk_response *)data)->member) +#define TALK_MSG(data, ver, member) (ver ? ((struct ntalk_msg *)data)->member : ((struct talk_msg *)data)->member) + +#define TALK_VERSION 0 /* protocol versions */ +#define NTALK_VERSION 1 +#define NTALK2_VERSION 2 + +/* message type values */ +#define LEAVE_INVITE 0 /* leave invitation with server */ +#define LOOK_UP 1 /* check for invitation by callee */ +#define DELETE 2 /* delete invitation by caller */ +#define ANNOUNCE 3 /* announce invitation by caller */ +/* NTALK2 */ +#define REPLY_QUERY 4 /* request reply data from local daemon */ + +/* answer values */ +#define SUCCESS 0 /* operation completed properly */ +#define NOT_HERE 1 /* callee not logged in */ +#define FAILED 2 /* operation failed for unexplained reason */ +#define MACHINE_UNKNOWN 3 /* caller's machine name unknown */ +#define PERMISSION_DENIED 4 /* callee's tty doesn't permit announce */ +#define UNKNOWN_REQUEST 5 /* request has invalid type value */ +#define BADVERSION 6 /* request has invalid protocol version */ +#define BADADDR 7 /* request has invalid addr value */ +#define BADCTLADDR 8 /* request has invalid ctl_addr value */ +/* NTALK2 */ +#define NO_CALLER 9 /* no-one calling answer from REPLY */ +#define TRY_HERE 10 /* Not on this machine, try this */ +#define SELECTIVE_REFUSAL 11 /* User Filter refusal. */ +#define MAX_RESPONSE_TYPE 11 /* Make sure this is updated */ + +/* We don't really need much for talk */ +struct ip_ct_talk_expect +{ + /* Port that was to be used */ + u_int16_t port; +}; + +/* This structure exists only once per master */ +struct ip_ct_talk_master +{ +}; + +#endif /* _IP_CONNTRACK_TALK_H */ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_tcp.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_tcp.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_conntrack_tcp.h 2000-08-04 22:07:24.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_conntrack_tcp.h 2002-07-07 00:19:39.000000000 +0200 @@ -8,25 +8,35 @@ enum tcp_conntrack { TCP_CONNTRACK_NONE, - TCP_CONNTRACK_ESTABLISHED, TCP_CONNTRACK_SYN_SENT, TCP_CONNTRACK_SYN_RECV, + TCP_CONNTRACK_ESTABLISHED, TCP_CONNTRACK_FIN_WAIT, - TCP_CONNTRACK_TIME_WAIT, - TCP_CONNTRACK_CLOSE, TCP_CONNTRACK_CLOSE_WAIT, TCP_CONNTRACK_LAST_ACK, + TCP_CONNTRACK_TIME_WAIT, + TCP_CONNTRACK_CLOSE, TCP_CONNTRACK_LISTEN, TCP_CONNTRACK_MAX }; +struct ip_ct_tcp_state { + u_int32_t td_end; /* max of seq + len */ + u_int32_t td_maxend; /* max of ack + max(win, 1) */ + u_int32_t td_maxwin; /* max(win) */ + u_int8_t td_scale; /* window scale factor */ +}; + struct ip_ct_tcp { - enum tcp_conntrack state; - - /* Poor man's window tracking: sequence number of valid ACK - handshake completion packet */ - u_int32_t handshake_ack; + enum tcp_conntrack state; /* state of the connection */ + struct ip_ct_tcp_state seen[2]; /* connection parameters per direction */ }; +/* For NAT, when it mangles the packet */ +extern void ip_conntrack_tcp_update(struct ip_conntrack *conntrack, int dir, + struct iphdr *iph, size_t newlen, + struct tcphdr *tcph); + + #endif /* _IP_CONNTRACK_TCP_H */ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_nat.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_nat.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_nat.h 2001-04-26 00:00:28.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_nat.h 2002-07-07 00:19:39.000000000 +0200 @@ -11,8 +11,13 @@ IP_NAT_MANIP_DST }; +#ifndef CONFIG_IP_NF_NAT_LOCAL /* SRC manip occurs only on POST_ROUTING */ #define HOOK2MANIP(hooknum) ((hooknum) != NF_IP_POST_ROUTING) +#else +/* SRC manip occurs POST_ROUTING or LOCAL_IN */ +#define HOOK2MANIP(hooknum) ((hooknum) != NF_IP_POST_ROUTING && (hooknum) != NF_IP_LOCAL_IN) +#endif /* 2.3.19 (I hope) will define this in linux/netfilter_ipv4.h. */ #ifndef SO_ORIGINAL_DST diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_nat_helper.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_nat_helper.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_nat_helper.h 2001-04-26 00:00:28.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_nat_helper.h 2002-07-07 00:19:39.000000000 +0200 @@ -6,23 +6,37 @@ struct sk_buff; +/* Flags */ +/* NAT helper must be called on every packet (for TCP) */ +#define IP_NAT_HELPER_F_ALWAYS 0x01 +/* Standalone NAT helper, without a conntrack part */ +#define IP_NAT_HELPER_F_STANDALONE 0x02 + struct ip_nat_helper { - /* Internal use */ - struct list_head list; + struct list_head list; /* Internal use */ + const char *name; /* name of the module */ + unsigned char flags; /* Flags (see above) */ + struct module *me; /* pointer to self */ + /* Mask of things we will help: vs. tuple from server */ struct ip_conntrack_tuple tuple; struct ip_conntrack_tuple mask; /* Helper function: returns verdict */ unsigned int (*help)(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, struct ip_nat_info *info, enum ip_conntrack_info ctinfo, unsigned int hooknum, struct sk_buff **pskb); - const char *name; + /* Returns verdict and sets up NAT for this connection */ + unsigned int (*expect)(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info); }; extern struct list_head helpers; @@ -39,5 +53,5 @@ extern int ip_nat_seq_adjust(struct sk_buff *skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo); -extern void ip_nat_delete_sack(struct sk_buff *skb, struct tcphdr *tcph); +extern void ip_nat_delete_sack(struct sk_buff *skb); #endif diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_nat_rule.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_nat_rule.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_nat_rule.h 2000-12-11 23:31:32.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_nat_rule.h 2002-07-07 00:19:39.000000000 +0200 @@ -5,24 +5,7 @@ #include #ifdef __KERNEL__ -/* Want to be told when we first NAT an expected packet for a conntrack? */ -struct ip_nat_expect -{ - struct list_head list; - /* Returns 1 (and sets verdict) if it has setup NAT for this - connection */ - int (*expect)(struct sk_buff **pskb, - unsigned int hooknum, - struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, - unsigned int *verdict); -}; - -extern int ip_nat_expect_register(struct ip_nat_expect *expect); -extern void ip_nat_expect_unregister(struct ip_nat_expect *expect); extern int ip_nat_rule_init(void) __init; extern void ip_nat_rule_cleanup(void); extern int ip_nat_rule_find(struct sk_buff **pskb, diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_pool.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_pool.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ip_pool.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ip_pool.h 2002-07-07 00:19:39.000000000 +0200 @@ -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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_DSCP.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_DSCP.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_DSCP.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_DSCP.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,22 @@ +/* iptables module for setting the IPv4 DSCP field + * + * (C) 2002 Harald Welte + * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh + * This software is distributed under GNU GPL v2, 1991 + * + * See RFC2474 for a description of the DSCP field within the IP Header. + * + * ipt_DSCP.h,v 1.6 2002/02/25 09:41:23 laforge Exp +*/ +#ifndef _IPT_DSCP_H +#define _IPT_DSCP_H + +#define IPT_DSCP_MASK 0xfc /* 11111100 */ +#define IPT_DSCP_SHIFT 2 /* shift DSCP two bits for ECN */ +#define IPT_DSCP_MAX 0x3f /* 00111111 */ + +struct ipt_DSCP_info { + u_int8_t dscp; +}; + +#endif /* _IPT_DSCP_H */ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_NETLINK.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_NETLINK.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_NETLINK.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_NETLINK.h 2002-07-07 00:19:39.000000000 +0200 @@ -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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_REJECT.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_REJECT.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_REJECT.h 2000-07-14 21:20:23.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_REJECT.h 2002-07-07 00:19:39.000000000 +0200 @@ -14,6 +14,7 @@ struct ipt_reject_info { enum ipt_reject_with with; /* reject type */ + u_int32_t fake_source_address; /* fake source address, 0 if no fake */ }; -#endif /*_IPT_REJECT_H*/ +#endif /* _IPT_REJECT_H */ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_SAME.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_SAME.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_SAME.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_SAME.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,22 @@ +#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; + + unsigned int rangesize; + + unsigned int ipnum; + + u_int32_t *iparray; + + /* hangs off end. */ + struct ip_nat_range range[IPT_SAME_MAX_RANGE]; +}; + +#endif /*_IPT_SAME_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_TTL.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_TTL.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_TTL.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_TTL.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,21 @@ +/* TTL modification module for IP tables + * (C) 2000 by Harald Welte */ + +#ifndef _IPT_TTL_H +#define _IPT_TTL_H + +enum { + IPT_TTL_SET = 0, + IPT_TTL_INC, + IPT_TTL_DEC +}; + +#define IPT_TTL_MAXMODE IPT_TTL_DEC + +struct ipt_TTL_info { + u_int8_t mode; + u_int8_t ttl; +}; + + +#endif diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_bytelimit.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_bytelimit.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_bytelimit.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_bytelimit.h 2002-07-07 00:20:46.000000000 +0200 @@ -0,0 +1,17 @@ +#ifndef _IPT_BYTELIMIT_H +#define _IPT_BYTELIMIT_H + +struct ipt_bytelimitinfo { + u_int32_t rate; /* rate, in bytes per second */ + u_int32_t offset; /* Added to each packet length. */ + u_int64_t quota; /* maximal credit */ + u_int64_t credit; /* current quota */ + + /* Used internally by the kernel */ + unsigned long prev; /* last jiffy when the match was tested */ + u_int32_t tickrate; /* rate, in bytes per jiffy */ + + /* Ugly, ugly fucker. */ + struct ipt_bytelimitinfo *master; +}; +#endif /*_IPT_BYTELIMIT_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_conntrack.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_conntrack.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_conntrack.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_conntrack.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,38 @@ +/* Header file for kernel module to match connection tracking information. + * GPL (C) 2001 Marc Boucher (marc@mbsi.ca). + */ + +#ifndef _IPT_CONNTRACK_H +#define _IPT_CONNTRACK_H + +#define IPT_CONNTRACK_STATE_BIT(ctinfo) (1 << ((ctinfo)%IP_CT_IS_REPLY+1)) +#define IPT_CONNTRACK_STATE_INVALID (1 << 0) + +#define IPT_CONNTRACK_STATE_SNAT (1 << (IP_CT_NUMBER + 1)) +#define IPT_CONNTRACK_STATE_DNAT (1 << (IP_CT_NUMBER + 2)) + +/* flags, invflags: */ +#define IPT_CONNTRACK_STATE 0x01 +#define IPT_CONNTRACK_PROTO 0x02 +#define IPT_CONNTRACK_ORIGSRC 0x04 +#define IPT_CONNTRACK_ORIGDST 0x08 +#define IPT_CONNTRACK_REPLSRC 0x10 +#define IPT_CONNTRACK_REPLDST 0x20 +#define IPT_CONNTRACK_STATUS 0x40 +#define IPT_CONNTRACK_EXPIRES 0x80 + +struct ipt_conntrack_info +{ + unsigned int statemask, statusmask; + + struct ip_conntrack_tuple tuple[IP_CT_DIR_MAX]; + struct in_addr sipmsk[IP_CT_DIR_MAX], dipmsk[IP_CT_DIR_MAX]; + + unsigned long expires_min, expires_max; + + /* Flags word */ + u_int8_t flags; + /* Inverse flags */ + u_int8_t invflags; +}; +#endif /*_IPT_CONNTRACK_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_dscp.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_dscp.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_dscp.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_dscp.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,23 @@ +/* iptables module for matching the IPv4 DSCP field + * + * (C) 2002 Harald Welte + * This software is distributed under GNU GPL v2, 1991 + * + * See RFC2474 for a description of the DSCP field within the IP Header. + * + * dscp.patch,v 1.1 2002/04/04 10:48:35 laforge Exp +*/ +#ifndef _IPT_DSCP_H +#define _IPT_DSCP_H + +#define IPT_DSCP_MASK 0xfc /* 11111100 */ +#define IPT_DSCP_SHIFT 2 /* shift DSCP two bits for ECN */ +#define IPT_DSCP_MAX 0x3f /* 00111111 */ + +/* match info */ +struct ipt_dscp_info { + u_int8_t dscp; + u_int8_t invert; +}; + +#endif /* _IPT_DSCP_H */ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_helper.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_helper.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_helper.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_helper.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,8 @@ +#ifndef _IPT_HELPER_H +#define _IPT_HELPER_H + +struct ipt_helper_info { + int invert; + char name[30]; +}; +#endif /* _IPT_HELPER_H */ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_ipv4options.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_ipv4options.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_ipv4options.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_ipv4options.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,21 @@ +#ifndef __ipt_ipv4options_h_included__ +#define __ipt_ipv4options_h_included__ + +#define IPT_IPV4OPTION_MATCH_SSRR 0x01 /* For strict source routing */ +#define IPT_IPV4OPTION_MATCH_LSRR 0x02 /* For loose source routing */ +#define IPT_IPV4OPTION_DONT_MATCH_SRR 0x04 /* any source routing */ +#define IPT_IPV4OPTION_MATCH_RR 0x08 /* For Record route */ +#define IPT_IPV4OPTION_DONT_MATCH_RR 0x10 +#define IPT_IPV4OPTION_MATCH_TIMESTAMP 0x20 /* For timestamp request */ +#define IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP 0x40 +#define IPT_IPV4OPTION_MATCH_ROUTER_ALERT 0x80 /* For router-alert */ +#define IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT 0x100 +#define IPT_IPV4OPTION_MATCH_ANY_OPT 0x200 /* match packet with any option */ +#define IPT_IPV4OPTION_DONT_MATCH_ANY_OPT 0x400 /* match packet with no option */ + +struct ipt_ipv4options_info { + u_int16_t options; +}; + + +#endif /* __ipt_ipv4options_h_included__ */ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_mark.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_mark.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_mark.h 2000-03-17 20:56:20.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_mark.h 2002-07-07 00:19:39.000000000 +0200 @@ -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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_mport.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_mport.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_mport.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_mport.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,24 @@ +#ifndef _IPT_MPORT_H +#define _IPT_MPORT_H +#include + +#define IPT_MPORT_SOURCE (1<<0) +#define IPT_MPORT_DESTINATION (1<<1) +#define IPT_MPORT_EITHER (IPT_MPORT_SOURCE|IPT_MPORT_DESTINATION) + +#define IPT_MULTI_PORTS 15 + +/* Must fit inside union ipt_matchinfo: 32 bytes */ +/* every entry in ports[] except for the last one has one bit in pflags + * associated with it. If this bit is set, the port is the first port of + * a portrange, with the next entry being the last. + * End of list is marked with pflags bit set and port=65535. + * If 14 ports are used (last one does not have a pflag), the last port + * is repeated to fill the last entry in ports[] */ +struct ipt_mport +{ + u_int8_t flags:2; /* Type of comparison */ + u_int16_t pflags:14; /* Port flags */ + u_int16_t ports[IPT_MULTI_PORTS]; /* Ports */ +}; +#endif /*_IPT_MPORT_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_nth.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_nth.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_nth.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_nth.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,19 @@ +#ifndef _IPT_NTH_H +#define _IPT_NTH_H + +#include +#include + +#ifndef IPT_NTH_NUM_COUNTERS +#define IPT_NTH_NUM_COUNTERS 16 +#endif + +struct ipt_nth_info { + u_int8_t every; + u_int8_t not; + u_int8_t startat; + u_int8_t counter; + u_int8_t packet; +}; + +#endif /*_IPT_NTH_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_owner.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_owner.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_owner.h 2000-03-17 20:56:20.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_owner.h 2002-07-07 00:19:39.000000000 +0200 @@ -6,12 +6,14 @@ #define IPT_OWNER_GID 0x02 #define IPT_OWNER_PID 0x04 #define IPT_OWNER_SID 0x08 +#define IPT_OWNER_COMM 0x10 struct ipt_owner_info { uid_t uid; gid_t gid; pid_t pid; pid_t sid; + char comm[16]; u_int8_t match, invert; /* flags */ }; diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_pkttype.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_pkttype.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_pkttype.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_pkttype.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,8 @@ +#ifndef _IPT_PKTTYPE_H +#define _IPT_PKTTYPE_H + +struct ipt_pkttype_info { + int pkttype; + int invert; +}; +#endif /*_IPT_PKTTYPE_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_pool.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_pool.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_pool.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_pool.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,25 @@ +#ifndef _IPT_POOL_H +#define _IPT_POOL_H + +#include + +#define IPT_POOL_INV_SRC 0x00000001 +#define IPT_POOL_INV_DST 0x00000002 +#define IPT_POOL_DEL_SRC 0x00000004 +#define IPT_POOL_DEL_DST 0x00000008 +#define IPT_POOL_INV_MOD_SRC 0x00000010 +#define IPT_POOL_INV_MOD_DST 0x00000020 +#define IPT_POOL_MOD_SRC_ACCEPT 0x00000040 +#define IPT_POOL_MOD_DST_ACCEPT 0x00000080 +#define IPT_POOL_MOD_SRC_DROP 0x00000100 +#define IPT_POOL_MOD_DST_DROP 0x00000200 + +/* match info */ +struct ipt_pool_info +{ + ip_pool_t src; + ip_pool_t dst; + unsigned flags; +}; + +#endif /*_IPT_POOL_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_psd.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_psd.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_psd.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_psd.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,40 @@ +#ifndef _IPT_PSD_H +#define _IPT_PSD_H + +#include +#include + +/* + * High port numbers have a lower weight to reduce the frequency of false + * positives, such as from passive mode FTP transfers. + */ +#define PORT_WEIGHT_PRIV 3 +#define PORT_WEIGHT_HIGH 1 + +/* + * Port scan detection thresholds: at least COUNT ports need to be scanned + * from the same source, with no longer than DELAY ticks between ports. + */ +#define SCAN_MIN_COUNT 7 +#define SCAN_MAX_COUNT (SCAN_MIN_COUNT * PORT_WEIGHT_PRIV) +#define SCAN_WEIGHT_THRESHOLD SCAN_MAX_COUNT +#define SCAN_DELAY_THRESHOLD (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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_quota.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_quota.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_quota.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_quota.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,11 @@ +#ifndef _IPT_QUOTA_H +#define _IPT_QUOTA_H + +/* print debug info in both kernel/netfilter module & iptable library */ +//#define DEBUG_IPT_QUOTA + +struct ipt_quota_info { + u_int64_t quota; +}; + +#endif /*_IPT_QUOTA_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_random.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_random.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_random.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_random.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,11 @@ +#ifndef _IPT_RAND_H +#define _IPT_RAND_H + +#include +#include + +struct ipt_rand_info { + u_int8_t average; +}; + +#endif /*_IPT_RAND_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_realm.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_realm.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_realm.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_realm.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,8 @@ +#ifndef _IPT_REALM_H +#define _IPT_REALM_H + +struct ipt_realm_info { + unsigned long id, mask; + u_int8_t invert; +}; +#endif /*_IPT_REALM_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_recent.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_recent.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_recent.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_recent.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,18 @@ +#ifndef _IPT_RECENT_H +#define _IPT_RECENT_H + +const static int IPT_RECENT_CHECK = 1; +const static int IPT_RECENT_SET = 2; +const static int IPT_RECENT_UPDATE = 4; +const static int IPT_RECENT_REMOVE = 8; +const static int IPT_RECENT_TTL = 16; + +struct ipt_recent_info { + u_int32_t seconds; + u_int32_t hit_count; + u_int8_t check_set; + u_int8_t invert; + char name[200]; +}; + +#endif /*_IPT_RECENT_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_string.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_string.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_string.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_string.h 2002-07-07 00:19:39.000000000 +0200 @@ -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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_time.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_time.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv4/ipt_time.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv4/ipt_time.h 2002-07-07 00:19:39.000000000 +0200 @@ -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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_REJECT.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_REJECT.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_REJECT.h 2000-06-20 23:32:27.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_REJECT.h 2002-07-07 00:19:39.000000000 +0200 @@ -2,15 +2,17 @@ #define _IP6T_REJECT_H enum ip6t_reject_with { - IP6T_ICMP_NET_UNREACHABLE, - IP6T_ICMP_HOST_UNREACHABLE, - IP6T_ICMP_PROT_UNREACHABLE, - IP6T_ICMP_PORT_UNREACHABLE, - IP6T_ICMP_ECHOREPLY + IP6T_ICMP6_NO_ROUTE, + IP6T_ICMP6_ADM_PROHIBITED, + IP6T_ICMP6_NOT_NEIGHBOUR, + IP6T_ICMP6_ADDR_UNREACH, + IP6T_ICMP6_PORT_UNREACH, + IP6T_ICMP6_ECHOREPLY, + IP6T_TCP_RESET }; struct ip6t_reject_info { enum ip6t_reject_with with; /* reject type */ }; -#endif /*_IPT_REJECT_H*/ +#endif /*_IP6T_REJECT_H*/ diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_ah.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_ah.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_ah.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_ah.h 2002-07-07 00:19:39.000000000 +0200 @@ -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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_esp.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_esp.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_esp.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_esp.h 2002-07-07 00:19:39.000000000 +0200 @@ -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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_frag.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_frag.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_frag.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_frag.h 2002-07-07 00:19:39.000000000 +0200 @@ -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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_ipv6header.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_ipv6header.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_ipv6header.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_ipv6header.h 2002-07-07 00:19:39.000000000 +0200 @@ -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 */ + + +#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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_length.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_length.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_length.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_length.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,10 @@ +#ifndef _IP6T_LENGTH_H +#define _IP6T_LENGTH_H + +struct ip6t_length_info { + u_int16_t min, max; + u_int8_t invert; +}; + +#endif /*_IP6T_LENGTH_H*/ + diff -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_opts.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_opts.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_opts.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_opts.h 2002-07-07 00:19:39.000000000 +0200 @@ -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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_rt.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_rt.h --- linux-2.4.18_vanilla/include/linux/netfilter_ipv6/ip6t_rt.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/netfilter_ipv6/ip6t_rt.h 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,42 @@ +#ifndef _IP6T_RT_H +#define _IP6T_RT_H + +/*#include */ + +#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 -u --new-file --recursive linux-2.4.18_vanilla/include/linux/sysctl.h linux-2.4.18_iptables-1.2.7-20020511/include/linux/sysctl.h --- linux-2.4.18_vanilla/include/linux/sysctl.h 2001-11-26 15:29:17.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/include/linux/sysctl.h 2002-07-07 00:19:39.000000000 +0200 @@ -231,6 +231,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, @@ -337,6 +338,28 @@ NET_IPV4_CONF_ARPFILTER=13 }; +enum +{ + NET_IPV4_NF_CONNTRACK_MAX=1, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_NONE=2, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT=3, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV=4, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED=5, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT=6, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT=7, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK=8, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT=9, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE=10, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LISTEN=11, + NET_IPV4_NF_CONNTRACK_TCP_LOG_INVALID_SCALE=12, + NET_IPV4_NF_CONNTRACK_TCP_LOG_OUT_OF_WINDOW=13, + NET_IPV4_NF_CONNTRACK_TCP_BE_LIBERAL=14, + NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT=15, + NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT_STREAM=16, + NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT=17, + NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT=18 +}; + /* /proc/sys/net/ipv6 */ enum { NET_IPV6_CONF=16, diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/arp.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/arp.c --- linux-2.4.18_vanilla/net/ipv4/arp.c 2001-09-07 20:01:20.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/arp.c 2002-07-07 00:19:39.000000000 +0200 @@ -112,7 +112,7 @@ #include #include - +#include /* * Interface to generic neighbour cache. @@ -561,7 +561,8 @@ arp_ptr+=dev->addr_len; memcpy(arp_ptr, &dest_ip, 4); - dev_queue_xmit(skb); + /* Send it off, maybe filter it using firewalling first. */ + NF_HOOK(NF_ARP, NF_ARP_OUT, skb, NULL, dev, dev_queue_xmit); return; out: @@ -574,45 +575,31 @@ } /* - * Receive an arp request by the device layer. + * Process an arp request. */ -int arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) +int arp_process(struct sk_buff *skb) { - struct arphdr *arp = skb->nh.arph; - unsigned char *arp_ptr= (unsigned char *)(arp+1); + struct net_device *dev = skb->dev; + struct in_device *in_dev = in_dev_get(dev); + struct arphdr *arp; + unsigned char *arp_ptr; struct rtable *rt; unsigned char *sha, *tha; u32 sip, tip; u16 dev_type = dev->type; int addr_type; - struct in_device *in_dev = in_dev_get(dev); struct neighbour *n; -/* - * The hardware length of the packet should match the hardware length - * of the device. Similarly, the hardware types should match. The - * device should be ARP-able. Also, if pln is not 4, then the lookup - * is not from an IP number. We can't currently handle this, so toss - * it. - */ - if (in_dev == NULL || - arp->ar_hln != dev->addr_len || - dev->flags & IFF_NOARP || - skb->pkt_type == PACKET_OTHERHOST || - skb->pkt_type == PACKET_LOOPBACK || - arp->ar_pln != 4) + /* arp_rcv below verifies the ARP header, verifies the device + * is ARP'able, and linearizes the SKB (if needed). + */ + + if (in_dev == NULL) goto out; - if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) - goto out_of_mem; - - if (skb_is_nonlinear(skb)) { - if (skb_linearize(skb, GFP_ATOMIC) != 0) - goto freeskb; - arp = skb->nh.arph; - arp_ptr= (unsigned char *)(arp+1); - } + arp = skb->nh.arph; + arp_ptr= (unsigned char *)(arp+1); switch (dev_type) { default: @@ -827,13 +814,41 @@ out: if (in_dev) in_dev_put(in_dev); -freeskb: kfree_skb(skb); -out_of_mem: return 0; } +/* + * Receive an arp request from the device layer. + */ + +int arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) +{ + struct arphdr *arp = skb->nh.arph; + + if (arp->ar_hln != dev->addr_len || + dev->flags & IFF_NOARP || + skb->pkt_type == PACKET_OTHERHOST || + skb->pkt_type == PACKET_LOOPBACK || + arp->ar_pln != 4) + goto freeskb; + + if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) + goto out_of_mem; + + if (skb_is_nonlinear(skb)) { + if (skb_linearize(skb, GFP_ATOMIC) != 0) + goto freeskb; + } + + return NF_HOOK(NF_ARP, NF_ARP_IN, skb, dev, NULL, arp_process); + +freeskb: + kfree_skb(skb); +out_of_mem: + return 0; +} /* * User level interface (ioctl, /proc) diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/Config.in linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/Config.in --- linux-2.4.18_vanilla/net/ipv4/netfilter/Config.in 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/Config.in 2002-07-07 00:20:46.000000000 +0200 @@ -7,6 +7,9 @@ tristate 'Connection tracking (required for masq/NAT)' CONFIG_IP_NF_CONNTRACK if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then dep_tristate ' FTP protocol support' CONFIG_IP_NF_FTP $CONFIG_IP_NF_CONNTRACK + dep_tristate ' talk protocol support' CONFIG_IP_NF_TALK $CONFIG_IP_NF_CONNTRACK + dep_tristate ' H.323 (netmeeting) support' CONFIG_IP_NF_H323 $CONFIG_IP_NF_CONNTRACK + dep_tristate ' Eggdrop bot support' CONFIG_IP_NF_EGG $CONFIG_IP_NF_CONNTRACK dep_tristate ' IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK fi @@ -17,25 +20,52 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; then # The simple matches. 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 ' MAC address match support' CONFIG_IP_NF_MATCH_MAC $CONFIG_IP_NF_IPTABLES + dep_tristate ' Packet type match support (EXPERIMENTAL)' 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 ' recent match support' CONFIG_IP_NF_MATCH_RECENT $CONFIG_IP_NF_IPTABLES + dep_tristate ' TIME match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_TIME $CONFIG_IP_NF_IPTABLES + dep_tristate ' random match support' CONFIG_IP_NF_MATCH_RANDOM $CONFIG_IP_NF_IPTABLES + dep_tristate ' psd match support' CONFIG_IP_NF_MATCH_PSD $CONFIG_IP_NF_IPTABLES + 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 ' 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 ' TTL match support' CONFIG_IP_NF_MATCH_TTL $CONFIG_IP_NF_IPTABLES dep_tristate ' tcpmss match support' CONFIG_IP_NF_MATCH_TCPMSS $CONFIG_IP_NF_IPTABLES if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then + dep_tristate ' Helper match support' CONFIG_IP_NF_MATCH_HELPER $CONFIG_IP_NF_IPTABLES + fi + dep_tristate ' realm match support' CONFIG_IP_NF_MATCH_REALM $CONFIG_IP_NF_IPTABLES + if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then dep_tristate ' Connection state match support' CONFIG_IP_NF_MATCH_STATE $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES + dep_tristate ' RPC match support' CONFIG_IP_NF_MATCH_RPC $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES + dep_tristate ' Connection tracking match support' CONFIG_IP_NF_MATCH_CONNTRACK $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES + dep_tristate ' Bytelimit match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_BYTELIMIT $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 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 fi @@ -47,6 +77,25 @@ define_bool CONFIG_IP_NF_NAT_NEEDED y dep_tristate ' MASQUERADE target support' CONFIG_IP_NF_TARGET_MASQUERADE $CONFIG_IP_NF_NAT dep_tristate ' REDIRECT target support' CONFIG_IP_NF_TARGET_REDIRECT $CONFIG_IP_NF_NAT + # If they want talk, set to $CONFIG_IP_NF_NAT (m or y), + # or $CONFIG_IP_NF_TALK (m or y), whichever is weaker. Argh. + if [ "$CONFIG_IP_NF_TALK" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_TALK m + else + if [ "$CONFIG_IP_NF_TALK" = "y" ]; then + define_tristate CONFIG_IP_NF_NAT_TALK $CONFIG_IP_NF_NAT + fi + fi + if [ "$CONFIG_IP_NF_H323" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_H323 m + else + if [ "$CONFIG_IP_NF_H323" = "y" ]; then + define_tristate CONFIG_IP_NF_NAT_H323 $CONFIG_IP_NF_NAT + fi + fi + dep_tristate ' SAME target support' CONFIG_IP_NF_TARGET_SAME $CONFIG_IP_NF_NAT + dep_tristate ' NETMAP target support' CONFIG_IP_NF_TARGET_NETMAP $CONFIG_IP_NF_NAT + bool ' NAT of local connections (READ HELP)' CONFIG_IP_NF_NAT_LOCAL if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' Basic SNMP-ALG support (EXPERIMENTAL)' CONFIG_IP_NF_NAT_SNMP_BASIC $CONFIG_IP_NF_NAT fi @@ -72,12 +121,13 @@ dep_tristate ' Packet mangling' CONFIG_IP_NF_MANGLE $CONFIG_IP_NF_IPTABLES if [ "$CONFIG_IP_NF_MANGLE" != "n" ]; then dep_tristate ' TOS target support' CONFIG_IP_NF_TARGET_TOS $CONFIG_IP_NF_MANGLE + 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 fi dep_tristate ' LOG target support' CONFIG_IP_NF_TARGET_LOG $CONFIG_IP_NF_IPTABLES - if [ "$CONFIG_NETLINK" != "n" ]; then - dep_tristate ' ULOG target support' CONFIG_IP_NF_TARGET_ULOG $CONFIG_NETLINK $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 diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/Makefile linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/Makefile --- linux-2.4.18_vanilla/net/ipv4/netfilter/Makefile 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/Makefile 2002-07-07 00:20:47.000000000 +0200 @@ -9,18 +9,18 @@ O_TARGET := netfilter.o -export-objs = ip_conntrack_standalone.o ip_conntrack_ftp.o ip_fw_compat.o ip_nat_standalone.o ip_tables.o +export-objs = ip_conntrack_standalone.o ip_fw_compat.o ip_nat_standalone.o ip_tables.o arp_tables.o # Multipart objects. list-multi := ip_conntrack.o iptable_nat.o ipfwadm.o ipchains.o # objects for the conntrack and NAT core (used by standalone and backw. compat) ip_nf_conntrack-objs := ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o -ip_nf_nat-objs := ip_nat_core.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o +ip_nf_nat-objs := ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o # objects for the standalone - connection tracking / NAT ip_conntrack-objs := ip_conntrack_standalone.o $(ip_nf_conntrack-objs) -iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o ip_nat_helper.o $(ip_nf_nat-objs) +iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o $(ip_nf_nat-objs) # objects for backwards compatibility mode ip_nf_compat-objs := ip_fw_compat.o ip_fw_compat_redir.o ip_fw_compat_masq.o $(ip_nf_conntrack-objs) $(ip_nf_nat-objs) @@ -31,15 +31,43 @@ # connection tracking obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o -# IRC support -obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o -obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o +ifdef CONFIG_IP_NF_NAT_NEEDED + export-objs += ip_conntrack_proto_tcp.o +endif + + +# talk protocol support +obj-$(CONFIG_IP_NF_TALK) += ip_conntrack_talk.o +obj-$(CONFIG_IP_NF_NAT_TALK) += ip_nat_talk.o +ifdef CONFIG_IP_NF_NAT_TALK + export-objs += ip_conntrack_talk.o +endif + + +# H.323 support +obj-$(CONFIG_IP_NF_H323) += ip_conntrack_h323.o +obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o +ifdef CONFIG_IP_NF_NAT_H323 + export-objs += ip_conntrack_h323.o +endif + # connection tracking helpers obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o +obj-$(CONFIG_IP_NF_EGG) += ip_conntrack_egg.o + +ifdef CONFIG_IP_NF_NAT_FTP + export-objs += ip_conntrack_ftp.o +endif + +obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o +ifdef CONFIG_IP_NF_NAT_IRC + export-objs += ip_conntrack_irc.o +endif # NAT helpers obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o +obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o # generic IP tables obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o @@ -50,33 +78,77 @@ obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o # matches +obj-$(CONFIG_IP_NF_MATCH_HELPER) += ipt_helper.o obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o +obj-$(CONFIG_IP_NF_MATCH_QUOTA) += ipt_quota.o obj-$(CONFIG_IP_NF_MATCH_MARK) += ipt_mark.o +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_RPC) += ip_conntrack_rpc_tcp.o ip_conntrack_rpc_udp.o ipt_record_rpc.o +export-objs += ip_conntrack_rpc_tcp.o ip_conntrack_rpc_udp.o + +obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o + + +obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o + + +obj-$(CONFIG_IP_NF_MATCH_RANDOM) += ipt_random.o + +obj-$(CONFIG_IP_NF_MATCH_PSD) += ipt_psd.o + +obj-$(CONFIG_IP_NF_MATCH_NTH) += ipt_nth.o + +obj-$(CONFIG_IP_NF_MATCH_IPV4OPTIONS) += ipt_ipv4options.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_TTL) += ipt_ttl.o obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o +obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o +obj-$(CONFIG_IP_NF_MATCH_BYTELIMIT) += ipt_bytelimit.o +obj-$(CONFIG_IP_NF_MATCH_STRING) += ipt_string.o obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o +obj-$(CONFIG_IP_NF_MATCH_REALM) += ipt_realm.o # targets obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o +obj-$(CONFIG_IP_NF_TARGET_DSCP) += ipt_DSCP.o obj-$(CONFIG_IP_NF_TARGET_MARK) += ipt_MARK.o obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.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_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 +# generic ARP tables +obj-$(CONFIG_IP_NF_ARPTABLES) += arp_tables.o + +# just filtering instance of ARP tables for now +obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o + # backwards compatibility obj-$(CONFIG_IP_NF_COMPAT_IPCHAINS) += ipchains.o obj-$(CONFIG_IP_NF_COMPAT_IPFWADM) += ipfwadm.o diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/arp_tables.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/arp_tables.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/arp_tables.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/arp_tables.c 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,1313 @@ +/* + * Packet matching code for ARP packets. + * + * Based heavily, if not almost entirely, upon ip_tables.c framework. + * + * Some ARP specific bits are: + * + * Copyright (C) 2002 David S. Miller (davem@redhat.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/*#define DEBUG_ARP_TABLES*/ +/*#define DEBUG_ARP_TABLES_USER*/ + +#ifdef DEBUG_ARP_TABLES +#define dprintf(format, args...) printk(format , ## args) +#else +#define dprintf(format, args...) +#endif + +#ifdef DEBUG_ARP_TABLES_USER +#define duprintf(format, args...) printk(format , ## args) +#else +#define duprintf(format, args...) +#endif + +#ifdef CONFIG_NETFILTER_DEBUG +#define ARP_NF_ASSERT(x) \ +do { \ + if (!(x)) \ + printk("ARP_NF_ASSERT: %s:%s:%u\n", \ + __FUNCTION__, __FILE__, __LINE__); \ +} while(0) +#else +#define ARP_NF_ASSERT(x) +#endif +#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) + +static DECLARE_MUTEX(arpt_mutex); + +#define ASSERT_READ_LOCK(x) ARP_NF_ASSERT(down_trylock(&arpt_mutex) != 0) +#define ASSERT_WRITE_LOCK(x) ARP_NF_ASSERT(down_trylock(&arpt_mutex) != 0) +#include +#include + +struct arpt_table_info { + unsigned int size; + unsigned int number; + unsigned int initial_entries; + unsigned int hook_entry[NF_ARP_NUMHOOKS]; + unsigned int underflow[NF_ARP_NUMHOOKS]; + char entries[0] __attribute__((aligned(SMP_CACHE_BYTES))); +}; + +static LIST_HEAD(arpt_target); +static LIST_HEAD(arpt_tables); +#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0) + +#ifdef CONFIG_SMP +#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p)) +#else +#define TABLE_OFFSET(t,p) 0 +#endif + +static inline int arp_devaddr_compare(const struct arpt_devaddr_info *ap, + char *hdr_addr, int len) +{ + int i, ret; + + if (len > ARPT_DEV_ADDR_LEN_MAX) + len = ARPT_DEV_ADDR_LEN_MAX; + + ret = 0; + for (i = 0; i < len; i++) + ret |= (hdr_addr[i] ^ ap->addr[i]) & ap->mask[i]; + + return (ret != 0); +} + +/* Returns whether packet matches rule or not. */ +static inline int arp_packet_match(const struct arphdr *arphdr, + struct net_device *dev, + const char *indev, + const char *outdev, + const struct arpt_arp *arpinfo) +{ + char *arpptr = (char *)(arphdr + 1); + char *src_devaddr, *tgt_devaddr; + u32 *src_ipaddr, *tgt_ipaddr; + int i, ret; + +#define FWINV(bool,invflg) ((bool) ^ !!(arpinfo->invflags & invflg)) + + if (FWINV((arphdr->ar_op & arpinfo->arpop_mask) != arpinfo->arpop, + ARPT_INV_ARPOP)) { + dprintf("ARP operation field mismatch.\n"); + dprintf("ar_op: %04x info->arpop: %04x info->arpop_mask: %04x\n", + arphdr->ar_op, arpinfo->arpop, arpinfo->arpop_mask); + return 0; + } + + if (FWINV((arphdr->ar_hrd & arpinfo->arhrd_mask) != arpinfo->arhrd, + ARPT_INV_ARPHRD)) { + dprintf("ARP hardware address format mismatch.\n"); + dprintf("ar_hrd: %04x info->arhrd: %04x info->arhrd_mask: %04x\n", + arphdr->ar_hrd, arpinfo->arhrd, arpinfo->arhrd_mask); + return 0; + } + + if (FWINV((arphdr->ar_pro & arpinfo->arpro_mask) != arpinfo->arpro, + ARPT_INV_ARPPRO)) { + dprintf("ARP protocol address format mismatch.\n"); + dprintf("ar_pro: %04x info->arpro: %04x info->arpro_mask: %04x\n", + arphdr->ar_pro, arpinfo->arpro, arpinfo->arpro_mask); + return 0; + } + + if (FWINV((arphdr->ar_hln & arpinfo->arhln_mask) != arpinfo->arhln, + ARPT_INV_ARPHLN)) { + 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); + } + + src_devaddr = arpptr; + arpptr += dev->addr_len; + src_ipaddr = (u32 *) arpptr; + arpptr += sizeof(u32); + tgt_devaddr = arpptr; + arpptr += dev->addr_len; + tgt_ipaddr = (u32 *) arpptr; + + if (FWINV(arp_devaddr_compare(&arpinfo->src_devaddr, src_devaddr, dev->addr_len), + ARPT_INV_SRCDEVADDR) || + FWINV(arp_devaddr_compare(&arpinfo->tgt_devaddr, tgt_devaddr, dev->addr_len), + ARPT_INV_TGTDEVADDR)) { + dprintf("Source or target device address mismatch.\n"); + + return 0; + } + + if (FWINV(((*src_ipaddr) & arpinfo->smsk.s_addr) != arpinfo->src.s_addr, + ARPT_INV_SRCIP) || + FWINV((((*tgt_ipaddr) & arpinfo->tmsk.s_addr) != arpinfo->tgt.s_addr), + ARPT_INV_TGTIP)) { + dprintf("Source or target IP address mismatch.\n"); + + dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n", + NIPQUAD(*src_ipaddr), + NIPQUAD(arpinfo->smsk.s_addr), + NIPQUAD(arpinfo->src.s_addr), + arpinfo->invflags & ARPT_INV_SRCIP ? " (INV)" : ""); + dprintf("TGT: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n", + NIPQUAD(*tgt_ipaddr), + NIPQUAD(arpinfo->tmsk.s_addr), + NIPQUAD(arpinfo->tgt.s_addr), + arpinfo->invflags & ARPT_INV_TGTIP ? " (INV)" : ""); + return 0; + } + + /* Look for ifname matches; this should unroll nicely. */ + for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) { + ret |= (((const unsigned long *)indev)[i] + ^ ((const unsigned long *)arpinfo->iniface)[i]) + & ((const unsigned long *)arpinfo->iniface_mask)[i]; + } + + if (FWINV(ret != 0, ARPT_INV_VIA_IN)) { + dprintf("VIA in mismatch (%s vs %s).%s\n", + indev, arpinfo->iniface, + arpinfo->invflags&ARPT_INV_VIA_IN ?" (INV)":""); + return 0; + } + + for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) { + ret |= (((const unsigned long *)outdev)[i] + ^ ((const unsigned long *)arpinfo->outiface)[i]) + & ((const unsigned long *)arpinfo->outiface_mask)[i]; + } + + if (FWINV(ret != 0, ARPT_INV_VIA_OUT)) { + dprintf("VIA out mismatch (%s vs %s).%s\n", + outdev, arpinfo->outiface, + arpinfo->invflags&ARPT_INV_VIA_OUT ?" (INV)":""); + return 0; + } + + return 1; +} + +static inline int arp_checkentry(const struct arpt_arp *arp) +{ + if (arp->flags & ~ARPT_F_MASK) { + duprintf("Unknown flag bits set: %08X\n", + arp->flags & ~ARPT_F_MASK); + return 0; + } + if (arp->invflags & ~ARPT_INV_MASK) { + duprintf("Unknown invflag bits set: %08X\n", + arp->invflags & ~ARPT_INV_MASK); + return 0; + } + + return 1; +} + +static unsigned int arpt_error(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + if (net_ratelimit()) + printk("arp_tables: error: '%s'\n", (char *)targinfo); + + return NF_DROP; +} + +static inline struct arpt_entry *get_entry(void *base, unsigned int offset) +{ + return (struct arpt_entry *)(base + offset); +} + +unsigned int arpt_do_table(struct sk_buff **pskb, + unsigned int hook, + const struct net_device *in, + const struct net_device *out, + struct arpt_table *table, + void *userdata) +{ + static const char nulldevname[IFNAMSIZ] = { 0 }; + unsigned int verdict = NF_DROP; + struct arphdr *arp = (*pskb)->nh.arph; + int hotdrop = 0; + struct arpt_entry *e, *back; + const char *indev, *outdev; + void *table_base; + + indev = in ? in->name : nulldevname; + outdev = out ? out->name : nulldevname; + + read_lock_bh(&table->lock); + table_base = (void *)table->private->entries + + TABLE_OFFSET(table->private, + cpu_number_map(smp_processor_id())); + e = get_entry(table_base, table->private->hook_entry[hook]); + back = get_entry(table_base, table->private->underflow[hook]); + + do { + if (arp_packet_match(arp, (*pskb)->dev, indev, outdev, &e->arp)) { + struct arpt_entry_target *t; + int hdr_len; + + hdr_len = sizeof(*arp) + (2 * sizeof(struct in_addr)) + + (2 * (*pskb)->dev->addr_len); + ADD_COUNTER(e->counters, hdr_len, 1); + + t = arpt_get_target(e); + + /* Standard target? */ + if (!t->u.kernel.target->target) { + int v; + + v = ((struct arpt_standard_target *)t)->verdict; + if (v < 0) { + /* Pop from stack? */ + if (v != ARPT_RETURN) { + verdict = (unsigned)(-v) - 1; + break; + } + e = back; + back = get_entry(table_base, + back->comefrom); + continue; + } + if (table_base + v + != (void *)e + e->next_offset) { + /* Save old back ptr in next entry */ + struct arpt_entry *next + = (void *)e + e->next_offset; + next->comefrom = + (void *)back - table_base; + + /* set back pointer to next entry */ + back = next; + } + + e = get_entry(table_base, v); + } else { + /* Targets which reenter must return + * abs. verdicts + */ + verdict = t->u.kernel.target->target(pskb, + hook, + in, out, + t->data, + userdata); + + /* Target might have changed stuff. */ + arp = (*pskb)->nh.arph; + + if (verdict == ARPT_CONTINUE) + e = (void *)e + e->next_offset; + else + /* Verdict */ + break; + } + } else { + e = (void *)e + e->next_offset; + } + } while (!hotdrop); + read_unlock_bh(&table->lock); + + if (hotdrop) + return NF_DROP; + else + return verdict; +} + +static inline void *find_inlist_lock_noload(struct list_head *head, + const char *name, + int *error, + struct semaphore *mutex) +{ + void *ret; + + *error = down_interruptible(mutex); + if (*error != 0) + return NULL; + + ret = list_named_find(head, name); + if (!ret) { + *error = -ENOENT; + up(mutex); + } + return ret; +} + +#ifndef CONFIG_KMOD +#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m)) +#else +static void * +find_inlist_lock(struct list_head *head, + const char *name, + const char *prefix, + int *error, + struct semaphore *mutex) +{ + void *ret; + + ret = find_inlist_lock_noload(head, name, error, mutex); + if (!ret) { + char modulename[ARPT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1]; + strcpy(modulename, prefix); + strcat(modulename, name); + duprintf("find_inlist: loading `%s'.\n", modulename); + request_module(modulename); + ret = find_inlist_lock_noload(head, name, error, mutex); + } + + return ret; +} +#endif + +static inline struct arpt_table *find_table_lock(const char *name, int *error, struct semaphore *mutex) +{ + return find_inlist_lock(&arpt_tables, name, "arptable_", error, mutex); +} + +static inline struct arpt_target *find_target_lock(const char *name, int *error, struct semaphore *mutex) +{ + return find_inlist_lock(&arpt_target, name, "arpt_", error, mutex); +} + +/* All zeroes == unconditional rule. */ +static inline int unconditional(const struct arpt_arp *arp) +{ + unsigned int i; + + for (i = 0; i < sizeof(*arp)/sizeof(__u32); i++) + if (((__u32 *)arp)[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 mark_source_chains(struct arpt_table_info *newinfo, unsigned int valid_hooks) +{ + unsigned int hook; + + /* 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_ARP_NUMHOOKS; hook++) { + unsigned int pos = newinfo->hook_entry[hook]; + struct arpt_entry *e + = (struct arpt_entry *)(newinfo->entries + pos); + + if (!(valid_hooks & (1 << hook))) + continue; + + /* Set initial back pointer. */ + e->counters.pcnt = pos; + + for (;;) { + struct arpt_standard_target *t + = (void *)arpt_get_target(e); + + if (e->comefrom & (1 << NF_ARP_NUMHOOKS)) { + printk("arptables: loop hook %u pos %u %08X.\n", + hook, pos, e->comefrom); + return 0; + } + e->comefrom + |= ((1 << hook) | (1 << NF_ARP_NUMHOOKS)); + + /* Unconditional return/END. */ + if (e->target_offset == sizeof(struct arpt_entry) + && (strcmp(t->target.u.user.name, + ARPT_STANDARD_TARGET) == 0) + && t->verdict < 0 + && unconditional(&e->arp)) { + unsigned int oldpos, size; + + /* Return: backtrack through the last + * big jump. + */ + do { + e->comefrom ^= (1<counters.pcnt; + e->counters.pcnt = 0; + + /* We're at the start. */ + if (pos == oldpos) + goto next; + + e = (struct arpt_entry *) + (newinfo->entries + pos); + } while (oldpos == pos + e->next_offset); + + /* Move along one */ + size = e->next_offset; + e = (struct arpt_entry *) + (newinfo->entries + pos + size); + e->counters.pcnt = pos; + pos += size; + } else { + int newpos = t->verdict; + + if (strcmp(t->target.u.user.name, + ARPT_STANDARD_TARGET) == 0 + && newpos >= 0) { + /* This a jump; chase it. */ + duprintf("Jump rule %u -> %u\n", + pos, newpos); + } else { + /* ... this is a fallthru */ + newpos = pos + e->next_offset; + } + e = (struct arpt_entry *) + (newinfo->entries + newpos); + e->counters.pcnt = pos; + pos = newpos; + } + } + next: + duprintf("Finished chain %u\n", hook); + } + return 1; +} + +static inline int standard_check(const struct arpt_entry_target *t, + unsigned int max_offset) +{ + struct arpt_standard_target *targ = (void *)t; + + /* Check standard info. */ + if (t->u.target_size + != ARPT_ALIGN(sizeof(struct arpt_standard_target))) { + duprintf("arpt_standard_check: target size %u != %Zu\n", + t->u.target_size, + ARPT_ALIGN(sizeof(struct arpt_standard_target))); + return 0; + } + + if (targ->verdict >= 0 + && targ->verdict > max_offset - sizeof(struct arpt_entry)) { + duprintf("arpt_standard_check: bad verdict (%i)\n", + targ->verdict); + return 0; + } + + if (targ->verdict < -NF_MAX_VERDICT - 1) { + duprintf("arpt_standard_check: bad negative verdict (%i)\n", + targ->verdict); + return 0; + } + return 1; +} + +static struct arpt_target arpt_standard_target; + +static inline int check_entry(struct arpt_entry *e, const char *name, unsigned int size, + unsigned int *i) +{ + struct arpt_entry_target *t; + struct arpt_target *target; + int ret; + + if (!arp_checkentry(&e->arp)) { + duprintf("arp_tables: arp check failed %p %s.\n", e, name); + return -EINVAL; + } + + t = arpt_get_target(e); + target = find_target_lock(t->u.user.name, &ret, &arpt_mutex); + if (!target) { + duprintf("check_entry: `%s' not found\n", t->u.user.name); + goto out; + } + if (target->me) + __MOD_INC_USE_COUNT(target->me); + t->u.kernel.target = target; + up(&arpt_mutex); + + if (t->u.kernel.target == &arpt_standard_target) { + if (!standard_check(t, size)) { + ret = -EINVAL; + goto out; + } + } else if (t->u.kernel.target->checkentry + && !t->u.kernel.target->checkentry(name, e, t->data, + t->u.target_size + - sizeof(*t), + e->comefrom)) { + if (t->u.kernel.target->me) + __MOD_DEC_USE_COUNT(t->u.kernel.target->me); + duprintf("arp_tables: check failed for `%s'.\n", + t->u.kernel.target->name); + ret = -EINVAL; + goto out; + } + + (*i)++; + return 0; + +out: + return ret; +} + +static inline int check_entry_size_and_hooks(struct arpt_entry *e, + struct arpt_table_info *newinfo, + unsigned char *base, + unsigned char *limit, + const unsigned int *hook_entries, + const unsigned int *underflows, + unsigned int *i) +{ + unsigned int h; + + if ((unsigned long)e % __alignof__(struct arpt_entry) != 0 + || (unsigned char *)e + sizeof(struct arpt_entry) >= limit) { + duprintf("Bad offset %p\n", e); + return -EINVAL; + } + + if (e->next_offset + < sizeof(struct arpt_entry) + sizeof(struct arpt_entry_target)) { + duprintf("checking: element %p size %u\n", + e, e->next_offset); + return -EINVAL; + } + + /* Check hooks & underflows */ + for (h = 0; h < NF_ARP_NUMHOOKS; h++) { + if ((unsigned char *)e - base == hook_entries[h]) + newinfo->hook_entry[h] = hook_entries[h]; + if ((unsigned char *)e - base == underflows[h]) + newinfo->underflow[h] = underflows[h]; + } + + /* FIXME: underflows must be unconditional, standard verdicts + < 0 (not ARPT_RETURN). --RR */ + + /* Clear counters and comefrom */ + e->counters = ((struct arpt_counters) { 0, 0 }); + e->comefrom = 0; + + (*i)++; + return 0; +} + +static inline int cleanup_entry(struct arpt_entry *e, unsigned int *i) +{ + struct arpt_entry_target *t; + + if (i && (*i)-- == 0) + return 1; + + t = arpt_get_target(e); + if (t->u.kernel.target->destroy) + t->u.kernel.target->destroy(t->data, + t->u.target_size - sizeof(*t)); + if (t->u.kernel.target->me) + __MOD_DEC_USE_COUNT(t->u.kernel.target->me); + + return 0; +} + +/* Checks and translates the user-supplied table segment (held in + * newinfo). + */ +static int translate_table(const char *name, + unsigned int valid_hooks, + struct arpt_table_info *newinfo, + unsigned int size, + unsigned int number, + const unsigned int *hook_entries, + const unsigned int *underflows) +{ + unsigned int i; + int ret; + + newinfo->size = size; + newinfo->number = number; + + /* Init all hooks to impossible value. */ + for (i = 0; i < NF_ARP_NUMHOOKS; i++) { + newinfo->hook_entry[i] = 0xFFFFFFFF; + newinfo->underflow[i] = 0xFFFFFFFF; + } + + duprintf("translate_table: size %u\n", newinfo->size); + i = 0; + + /* Walk through entries, checking offsets. */ + ret = ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, + check_entry_size_and_hooks, + newinfo, + newinfo->entries, + newinfo->entries + size, + hook_entries, underflows, &i); + duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret); + if (ret != 0) + return ret; + + if (i != number) { + duprintf("translate_table: %u not %u entries\n", + i, number); + return -EINVAL; + } + + /* Check hooks all assigned */ + for (i = 0; i < NF_ARP_NUMHOOKS; i++) { + /* Only hooks which are valid */ + if (!(valid_hooks & (1 << i))) + continue; + if (newinfo->hook_entry[i] == 0xFFFFFFFF) { + duprintf("Invalid hook entry %u %u\n", + i, hook_entries[i]); + return -EINVAL; + } + if (newinfo->underflow[i] == 0xFFFFFFFF) { + duprintf("Invalid underflow %u %u\n", + i, underflows[i]); + return -EINVAL; + } + } + + if (!mark_source_chains(newinfo, valid_hooks)) { + duprintf("Looping hook\n"); + return -ELOOP; + } + + /* Finally, each sanity check must pass */ + i = 0; + ret = ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, + check_entry, name, size, &i); + + if (ret != 0) { + ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, + cleanup_entry, &i); + return ret; + } + + /* And one copy for every other CPU */ + for (i = 1; i < smp_num_cpus; i++) { + memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i, + newinfo->entries, + SMP_ALIGN(newinfo->size)); + } + + return ret; +} + +static struct arpt_table_info *replace_table(struct arpt_table *table, + unsigned int num_counters, + struct arpt_table_info *newinfo, + int *error) +{ + struct arpt_table_info *oldinfo; + + /* Do the substitution. */ + write_lock_bh(&table->lock); + /* Check inside lock: is the old number correct? */ + if (num_counters != table->private->number) { + duprintf("num_counters != table->private->number (%u/%u)\n", + num_counters, table->private->number); + write_unlock_bh(&table->lock); + *error = -EAGAIN; + return NULL; + } + oldinfo = table->private; + table->private = newinfo; + newinfo->initial_entries = oldinfo->initial_entries; + write_unlock_bh(&table->lock); + + return oldinfo; +} + +/* Gets counters. */ +static inline int add_entry_to_counter(const struct arpt_entry *e, + struct arpt_counters total[], + unsigned int *i) +{ + ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt); + + (*i)++; + return 0; +} + +static void get_counters(const struct arpt_table_info *t, + struct arpt_counters counters[]) +{ + unsigned int cpu; + unsigned int i; + + for (cpu = 0; cpu < smp_num_cpus; cpu++) { + i = 0; + ARPT_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu), + t->size, + add_entry_to_counter, + counters, + &i); + } +} + +static int copy_entries_to_user(unsigned int total_size, + struct arpt_table *table, + void *userptr) +{ + unsigned int off, num, countersize; + struct arpt_entry *e; + struct arpt_counters *counters; + int ret = 0; + + /* We need atomic snapshot of counters: rest doesn't change + * (other than comefrom, which userspace doesn't care + * about). + */ + countersize = sizeof(struct arpt_counters) * table->private->number; + counters = vmalloc(countersize); + + if (counters == NULL) + return -ENOMEM; + + /* First, sum counters... */ + memset(counters, 0, countersize); + write_lock_bh(&table->lock); + get_counters(table->private, counters); + write_unlock_bh(&table->lock); + + /* ... then copy entire thing from CPU 0... */ + if (copy_to_user(userptr, table->private->entries, total_size) != 0) { + ret = -EFAULT; + goto free_counters; + } + + /* FIXME: use iterator macros --RR */ + /* ... then go back and fix counters and names */ + for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){ + struct arpt_entry_target *t; + + e = (struct arpt_entry *)(table->private->entries + off); + if (copy_to_user(userptr + off + + offsetof(struct arpt_entry, counters), + &counters[num], + sizeof(counters[num])) != 0) { + ret = -EFAULT; + goto free_counters; + } + + t = arpt_get_target(e); + if (copy_to_user(userptr + off + e->target_offset + + offsetof(struct arpt_entry_target, + u.user.name), + t->u.kernel.target->name, + strlen(t->u.kernel.target->name)+1) != 0) { + ret = -EFAULT; + goto free_counters; + } + } + + free_counters: + vfree(counters); + return ret; +} + +static int get_entries(const struct arpt_get_entries *entries, + struct arpt_get_entries *uptr) +{ + int ret; + struct arpt_table *t; + + t = find_table_lock(entries->name, &ret, &arpt_mutex); + if (t) { + duprintf("t->private->number = %u\n", + t->private->number); + if (entries->size == t->private->size) + ret = copy_entries_to_user(t->private->size, + t, uptr->entrytable); + else { + duprintf("get_entries: I've got %u not %u!\n", + t->private->size, + entries->size); + ret = -EINVAL; + } + up(&arpt_mutex); + } else + duprintf("get_entries: Can't find %s!\n", + entries->name); + + return ret; +} + +static int do_replace(void *user, unsigned int len) +{ + int ret; + struct arpt_replace tmp; + struct arpt_table *t; + struct arpt_table_info *newinfo, *oldinfo; + struct arpt_counters *counters; + + if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) + return -EFAULT; + + /* Hack: Causes ipchains to give correct error msg --RR */ + if (len != sizeof(tmp) + tmp.size) + return -ENOPROTOOPT; + + /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */ + if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages) + return -ENOMEM; + + newinfo = vmalloc(sizeof(struct arpt_table_info) + + SMP_ALIGN(tmp.size) * smp_num_cpus); + if (!newinfo) + return -ENOMEM; + + if (copy_from_user(newinfo->entries, user + sizeof(tmp), + tmp.size) != 0) { + ret = -EFAULT; + goto free_newinfo; + } + + counters = vmalloc(tmp.num_counters * sizeof(struct arpt_counters)); + if (!counters) { + ret = -ENOMEM; + goto free_newinfo; + } + memset(counters, 0, tmp.num_counters * sizeof(struct arpt_counters)); + + ret = translate_table(tmp.name, tmp.valid_hooks, + newinfo, tmp.size, tmp.num_entries, + tmp.hook_entry, tmp.underflow); + if (ret != 0) + goto free_newinfo_counters; + + duprintf("arp_tables: Translated table\n"); + + t = find_table_lock(tmp.name, &ret, &arpt_mutex); + if (!t) + goto free_newinfo_counters_untrans; + + /* You lied! */ + if (tmp.valid_hooks != t->valid_hooks) { + duprintf("Valid hook crap: %08X vs %08X\n", + tmp.valid_hooks, t->valid_hooks); + ret = -EINVAL; + goto free_newinfo_counters_untrans_unlock; + } + + oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret); + if (!oldinfo) + goto free_newinfo_counters_untrans_unlock; + + /* Update module usage count based on number of rules */ + duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n", + oldinfo->number, oldinfo->initial_entries, newinfo->number); + if (t->me && (oldinfo->number <= oldinfo->initial_entries) && + (newinfo->number > oldinfo->initial_entries)) + __MOD_INC_USE_COUNT(t->me); + else if (t->me && (oldinfo->number > oldinfo->initial_entries) && + (newinfo->number <= oldinfo->initial_entries)) + __MOD_DEC_USE_COUNT(t->me); + + /* Get the old counters. */ + get_counters(oldinfo, counters); + /* Decrease module usage counts and free resource */ + ARPT_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL); + vfree(oldinfo); + /* Silent error: too late now. */ + copy_to_user(tmp.counters, counters, + sizeof(struct arpt_counters) * tmp.num_counters); + vfree(counters); + up(&arpt_mutex); + return 0; + + free_newinfo_counters_untrans_unlock: + up(&arpt_mutex); + free_newinfo_counters_untrans: + ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry, NULL); + free_newinfo_counters: + vfree(counters); + free_newinfo: + vfree(newinfo); + return ret; +} + +/* We're lazy, and add to the first CPU; overflow works its fey magic + * and everything is OK. + */ +static inline int add_counter_to_entry(struct arpt_entry *e, + const struct arpt_counters addme[], + unsigned int *i) +{ + + ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt); + + (*i)++; + return 0; +} + +static int do_add_counters(void *user, unsigned int len) +{ + unsigned int i; + struct arpt_counters_info tmp, *paddc; + struct arpt_table *t; + int ret; + + if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) + return -EFAULT; + + if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct arpt_counters)) + return -EINVAL; + + paddc = vmalloc(len); + if (!paddc) + return -ENOMEM; + + if (copy_from_user(paddc, user, len) != 0) { + ret = -EFAULT; + goto free; + } + + t = find_table_lock(tmp.name, &ret, &arpt_mutex); + if (!t) + goto free; + + write_lock_bh(&t->lock); + if (t->private->number != paddc->num_counters) { + ret = -EINVAL; + goto unlock_up_free; + } + + i = 0; + ARPT_ENTRY_ITERATE(t->private->entries, + t->private->size, + add_counter_to_entry, + paddc->counters, + &i); + unlock_up_free: + write_unlock_bh(&t->lock); + up(&arpt_mutex); + free: + vfree(paddc); + + return ret; +} + +static int do_arpt_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len) +{ + int ret; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + switch (cmd) { + case ARPT_SO_SET_REPLACE: + ret = do_replace(user, len); + break; + + case ARPT_SO_SET_ADD_COUNTERS: + ret = do_add_counters(user, len); + break; + + default: + duprintf("do_arpt_set_ctl: unknown request %i\n", cmd); + ret = -EINVAL; + } + + return ret; +} + +static int do_arpt_get_ctl(struct sock *sk, int cmd, void *user, int *len) +{ + int ret; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + switch (cmd) { + case ARPT_SO_GET_INFO: { + char name[ARPT_TABLE_MAXNAMELEN]; + struct arpt_table *t; + + if (*len != sizeof(struct arpt_getinfo)) { + duprintf("length %u != %Zu\n", *len, + sizeof(struct arpt_getinfo)); + ret = -EINVAL; + break; + } + + if (copy_from_user(name, user, sizeof(name)) != 0) { + ret = -EFAULT; + break; + } + name[ARPT_TABLE_MAXNAMELEN-1] = '\0'; + t = find_table_lock(name, &ret, &arpt_mutex); + if (t) { + struct arpt_getinfo info; + + info.valid_hooks = t->valid_hooks; + memcpy(info.hook_entry, t->private->hook_entry, + sizeof(info.hook_entry)); + memcpy(info.underflow, t->private->underflow, + sizeof(info.underflow)); + info.num_entries = t->private->number; + info.size = t->private->size; + strcpy(info.name, name); + + if (copy_to_user(user, &info, *len) != 0) + ret = -EFAULT; + else + ret = 0; + + up(&arpt_mutex); + } + } + break; + + case ARPT_SO_GET_ENTRIES: { + struct arpt_get_entries get; + + if (*len < sizeof(get)) { + duprintf("get_entries: %u < %Zu\n", *len, sizeof(get)); + ret = -EINVAL; + } else if (copy_from_user(&get, user, sizeof(get)) != 0) { + ret = -EFAULT; + } else if (*len != sizeof(struct arpt_get_entries) + get.size) { + duprintf("get_entries: %u != %Zu\n", *len, + sizeof(struct arpt_get_entries) + get.size); + ret = -EINVAL; + } else + ret = get_entries(&get, user); + break; + } + + default: + duprintf("do_arpt_get_ctl: unknown request %i\n", cmd); + ret = -EINVAL; + } + + return ret; +} + +/* Registration hooks for targets. */ +int arpt_register_target(struct arpt_target *target) +{ + int ret; + + MOD_INC_USE_COUNT; + ret = down_interruptible(&arpt_mutex); + if (ret != 0) { + MOD_DEC_USE_COUNT; + return ret; + } + if (!list_named_insert(&arpt_target, target)) { + duprintf("arpt_register_target: `%s' already in list!\n", + target->name); + ret = -EINVAL; + MOD_DEC_USE_COUNT; + } + up(&arpt_mutex); + return ret; +} + +void arpt_unregister_target(struct arpt_target *target) +{ + down(&arpt_mutex); + LIST_DELETE(&arpt_target, target); + up(&arpt_mutex); + MOD_DEC_USE_COUNT; +} + +int arpt_register_table(struct arpt_table *table) +{ + int ret; + struct arpt_table_info *newinfo; + static struct arpt_table_info bootstrap + = { 0, 0, 0, { 0 }, { 0 }, { } }; + + MOD_INC_USE_COUNT; + newinfo = vmalloc(sizeof(struct arpt_table_info) + + SMP_ALIGN(table->table->size) * smp_num_cpus); + if (!newinfo) { + ret = -ENOMEM; + MOD_DEC_USE_COUNT; + return ret; + } + memcpy(newinfo->entries, table->table->entries, table->table->size); + + ret = translate_table(table->name, table->valid_hooks, + newinfo, table->table->size, + table->table->num_entries, + table->table->hook_entry, + table->table->underflow); + duprintf("arpt_register_table: translate table gives %d\n", ret); + if (ret != 0) { + vfree(newinfo); + MOD_DEC_USE_COUNT; + return ret; + } + + ret = down_interruptible(&arpt_mutex); + if (ret != 0) { + vfree(newinfo); + MOD_DEC_USE_COUNT; + return ret; + } + + /* Don't autoload: we'd eat our tail... */ + if (list_named_find(&arpt_tables, table->name)) { + ret = -EEXIST; + goto free_unlock; + } + + /* Simplifies replace_table code. */ + table->private = &bootstrap; + if (!replace_table(table, 0, newinfo, &ret)) + goto free_unlock; + + duprintf("table->private->number = %u\n", + table->private->number); + + /* save number of initial entries */ + table->private->initial_entries = table->private->number; + + table->lock = RW_LOCK_UNLOCKED; + list_prepend(&arpt_tables, table); + + unlock: + up(&arpt_mutex); + return ret; + + free_unlock: + vfree(newinfo); + MOD_DEC_USE_COUNT; + goto unlock; +} + +void arpt_unregister_table(struct arpt_table *table) +{ + down(&arpt_mutex); + LIST_DELETE(&arpt_tables, table); + up(&arpt_mutex); + + /* Decrease module usage counts and free resources */ + ARPT_ENTRY_ITERATE(table->private->entries, table->private->size, + cleanup_entry, NULL); + vfree(table->private); + MOD_DEC_USE_COUNT; +} + +/* The built-in targets: standard (NULL) and error. */ +static struct arpt_target arpt_standard_target += { { NULL, NULL }, ARPT_STANDARD_TARGET, NULL, NULL, NULL }; +static struct arpt_target arpt_error_target += { { NULL, NULL }, ARPT_ERROR_TARGET, arpt_error, NULL, NULL }; + +static struct nf_sockopt_ops arpt_sockopts += { { NULL, NULL }, PF_INET, ARPT_BASE_CTL, ARPT_SO_SET_MAX+1, do_arpt_set_ctl, + ARPT_BASE_CTL, ARPT_SO_GET_MAX+1, do_arpt_get_ctl, 0, NULL }; + +#ifdef CONFIG_PROC_FS +static inline int print_name(const struct arpt_table *t, + 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); + if (*pos + namelen > length) { + /* Stop iterating */ + return 1; + } + *pos += namelen; + } + return 0; +} + +static int arpt_get_tables(char *buffer, char **start, off_t offset, int length) +{ + off_t pos = 0; + unsigned int count = 0; + + if (down_interruptible(&arpt_mutex) != 0) + return 0; + + LIST_FIND(&arpt_tables, print_name, struct arpt_table *, + offset, buffer, length, &pos, &count); + + up(&arpt_mutex); + + /* `start' hack - see fs/proc/generic.c line ~105 */ + *start=(char *)((unsigned long)count-offset); + return pos; +} +#endif /*CONFIG_PROC_FS*/ + +static int __init init(void) +{ + int ret; + + /* Noone else will be downing sem now, so we won't sleep */ + down(&arpt_mutex); + list_append(&arpt_target, &arpt_standard_target); + list_append(&arpt_target, &arpt_error_target); + up(&arpt_mutex); + + /* Register setsockopt */ + ret = nf_register_sockopt(&arpt_sockopts); + if (ret < 0) { + duprintf("Unable to register sockopts.\n"); + return ret; + } + +#ifdef CONFIG_PROC_FS + { + struct proc_dir_entry *proc; + + proc = proc_net_create("arp_tables_names", 0, arpt_get_tables); + if (!proc) { + nf_unregister_sockopt(&arpt_sockopts); + return -ENOMEM; + } + proc->owner = THIS_MODULE; + } +#endif + + printk("arp_tables: (C) 2002 David S. Miller\n"); + return 0; +} + +static void __exit fini(void) +{ + nf_unregister_sockopt(&arpt_sockopts); +#ifdef CONFIG_PROC_FS + proc_net_remove("arp_tables_names"); +#endif +} + +EXPORT_SYMBOL(arpt_register_table); +EXPORT_SYMBOL(arpt_unregister_table); +EXPORT_SYMBOL(arpt_do_table); +EXPORT_SYMBOL(arpt_register_target); +EXPORT_SYMBOL(arpt_unregister_target); + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/arptable_filter.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/arptable_filter.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/arptable_filter.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/arptable_filter.c 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,174 @@ +/* + * Filtering ARP tables module. + * + * Copyright (C) 2002 David S. Miller (davem@redhat.com) + * + */ + +#include +#include + +#define FILTER_VALID_HOOKS ((1 << NF_ARP_IN) | (1 << NF_ARP_OUT)) + +/* Standard entry. */ +struct arpt_standard +{ + struct arpt_entry entry; + struct arpt_standard_target target; +}; + +struct arpt_error_target +{ + struct arpt_entry_target target; + char errorname[ARPT_FUNCTION_MAXNAMELEN]; +}; + +struct arpt_error +{ + struct arpt_entry entry; + struct arpt_error_target target; +}; + +static struct +{ + struct arpt_replace repl; + struct arpt_standard entries[2]; + struct arpt_error term; +} initial_table __initdata += { { "filter", FILTER_VALID_HOOKS, 3, + sizeof(struct arpt_standard) * 2 + sizeof(struct arpt_error), + { [NF_ARP_IN] 0, + [NF_ARP_OUT] sizeof(struct arpt_standard) }, + { [NF_ARP_IN] 0, + [NF_ARP_OUT] sizeof(struct arpt_standard), }, + 0, NULL, { } }, + { + /* ARP_IN */ + { + { + { + { 0 }, { 0 }, { 0 }, { 0 }, + 0, 0, + { { 0, }, { 0, } }, + { { 0, }, { 0, } }, + 0, 0, + 0, 0, + 0, 0, + "", "", { 0 }, { 0 }, + 0, 0 + }, + sizeof(struct arpt_entry), + sizeof(struct arpt_standard), + 0, + { 0, 0 }, { } }, + { { { { ARPT_ALIGN(sizeof(struct arpt_standard_target)), "" } }, { } }, + -NF_ACCEPT - 1 } + }, + /* ARP_OUT */ + { + { + { + { 0 }, { 0 }, { 0 }, { 0 }, + 0, 0, + { { 0, }, { 0, } }, + { { 0, }, { 0, } }, + 0, 0, + 0, 0, + 0, 0, + "", "", { 0 }, { 0 }, + 0, 0 + }, + sizeof(struct arpt_entry), + sizeof(struct arpt_standard), + 0, + { 0, 0 }, { } }, + { { { { ARPT_ALIGN(sizeof(struct arpt_standard_target)), "" } }, { } }, + -NF_ACCEPT - 1 } + } + }, + /* ERROR */ + { + { + { + { 0 }, { 0 }, { 0 }, { 0 }, + 0, 0, + { { 0, }, { 0, } }, + { { 0, }, { 0, } }, + 0, 0, + 0, 0, + 0, 0, + "", "", { 0 }, { 0 }, + 0, 0 + }, + sizeof(struct arpt_entry), + sizeof(struct arpt_error), + 0, + { 0, 0 }, { } }, + { { { { ARPT_ALIGN(sizeof(struct arpt_error_target)), ARPT_ERROR_TARGET } }, + { } }, + "ERROR" + } + } +}; + +static struct arpt_table packet_filter += { { NULL, NULL }, "filter", &initial_table.repl, + FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE }; + +/* The work comes in here from netfilter.c */ +static unsigned int arpt_hook(unsigned int hook, + struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return arpt_do_table(pskb, hook, in, out, &packet_filter, NULL); +} + +static struct nf_hook_ops arpt_ops[] += { { { NULL, NULL }, arpt_hook, NF_ARP, NF_ARP_IN, 0 }, + { { NULL, NULL }, arpt_hook, NF_ARP, NF_ARP_OUT, 0 } +}; + +static int __init init(void) +{ + int ret; + + /* Register table */ + ret = arpt_register_table(&packet_filter); + if (ret < 0) + return ret; + + /* Register hooks */ + ret = nf_register_hook(&arpt_ops[0]); + if (ret < 0) + goto cleanup_table; + + ret = nf_register_hook(&arpt_ops[1]); + if (ret < 0) + goto cleanup_hook0; + + return ret; + +cleanup_hook0: + nf_unregister_hook(&arpt_ops[0]); + +cleanup_table: + arpt_unregister_table(&packet_filter); + + return ret; +} + +static void __exit fini(void) +{ + unsigned int i; + + for (i = 0; i < sizeof(arpt_ops)/sizeof(struct nf_hook_ops); i++) + nf_unregister_hook(&arpt_ops[i]); + + arpt_unregister_table(&packet_filter); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_core.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_core.c 2001-08-07 17:30:50.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_core.c 2002-07-07 00:19:39.000000000 +0200 @@ -3,7 +3,12 @@ extension. */ /* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General - Public Licence. */ + * Public Licence. + * + * 23 Apr 2001: Harald Welte + * - new API and handling of conntrack/nat helpers + * - now capable of multiple expectations for one master + * */ #ifdef MODULE #define __NO_VERSION__ @@ -37,6 +42,8 @@ #include #include +#define IP_CONNTRACK_VERSION "2.0" + #if 0 #define DEBUGP printk #else @@ -44,13 +51,14 @@ #endif DECLARE_RWLOCK(ip_conntrack_lock); +DECLARE_RWLOCK(ip_conntrack_expect_tuple_lock); void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL; LIST_HEAD(expect_list); 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; @@ -76,7 +84,7 @@ return p; } -struct ip_conntrack_protocol *find_proto(u_int8_t protocol) +struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol) { struct ip_conntrack_protocol *p; @@ -150,9 +158,58 @@ return protocol->invert_tuple(inverse, orig); } +/* remove one specific expectation from all lists and free it */ +static void unexpect_related(struct ip_conntrack_expect *expect) +{ + MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); + DEBUGP("unexpect_related(%p)\n", expect); + /* delete from global and local lists */ + list_del(&expect->list); + list_del(&expect->expected_list); + if (!expect->sibling) + expect->expectant->expecting--; + kfree(expect); +} + +/* delete all expectations for this conntrack */ +static void destroy_expectations(struct ip_conntrack *ct) +{ + struct list_head *exp_entry, *next; + struct ip_conntrack_expect *exp; + + DEBUGP("destroy_expectations(%p)\n", ct); + + for (exp_entry = ct->sibling_list.next; + exp_entry != &ct->sibling_list; exp_entry = next) { + next = exp_entry->next; + exp = list_entry(exp_entry, struct ip_conntrack_expect, + expected_list); + + /* we skip established expectations, as we want to delete + * the un-established ones only */ + if (exp->sibling) { + DEBUGP("destroy_expectations: skipping established %p of %p\n", exp->sibling, ct); + continue; + } + + IP_NF_ASSERT(list_inlist(&expect_list, exp)); + IP_NF_ASSERT(exp->expectant == ct); + + if (exp->expectant->helper->timeout + && ! del_timer(&exp->timeout)) { + DEBUGP("destroy_expectations: skipping dying expectation %p of %p\n", exp, ct); + continue; + } + + /* delete expectation from global and private lists */ + unexpect_related(exp); + } +} + static void clean_from_lists(struct ip_conntrack *ct) { + DEBUGP("clean_from_lists(%p)\n", ct); MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); /* Remove from both hash lists: must not NULL out next ptrs, otherwise we'll look unconfirmed. Fortunately, LIST_DELETE @@ -163,27 +220,45 @@ LIST_DELETE(&ip_conntrack_hash [hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple)], &ct->tuplehash[IP_CT_DIR_REPLY]); - /* If our expected is in the list, take it out. */ - if (ct->expected.expectant) { - IP_NF_ASSERT(list_inlist(&expect_list, &ct->expected)); - IP_NF_ASSERT(ct->expected.expectant == ct); - LIST_DELETE(&expect_list, &ct->expected); - } + + /* Destroy all un-established, pending expectations */ + destroy_expectations(ct); } static void destroy_conntrack(struct nf_conntrack *nfct) { struct ip_conntrack *ct = (struct ip_conntrack *)nfct; + struct ip_conntrack_protocol *proto; + DEBUGP("destroy_conntrack(%p)\n", ct); IP_NF_ASSERT(atomic_read(&nfct->use) == 0); IP_NF_ASSERT(!timer_pending(&ct->timeout)); - if (ct->master.master) - nf_conntrack_put(&ct->master); + if (ct->master && master_ct(ct)) + ip_conntrack_put(master_ct(ct)); + + /* 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 */ + proto = ip_ct_find_proto(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum); + if (proto && proto->destroy) + proto->destroy(ct); if (ip_conntrack_destroyed) ip_conntrack_destroyed(ct); + + WRITE_LOCK(&ip_conntrack_lock); + /* Delete our master expectation from the local list + * and destroy it, if we've been expected */ + if (ct->master) { + list_del(&ct->master->expected_list); + kfree(ct->master); + } + WRITE_UNLOCK(&ip_conntrack_lock); + + + DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct); kmem_cache_free(ip_conntrack_cachep, ct); atomic_dec(&ip_conntrack_count); } @@ -381,7 +456,7 @@ return NULL; } - innerproto = find_proto(inner->protocol); + innerproto = ip_ct_find_proto(inner->protocol); /* Are they talking about one of our connections? */ if (inner->ihl * 4 + 8 > datalen || !get_tuple(inner, datalen, &origtuple, innerproto)) { @@ -461,10 +536,18 @@ return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask); } +struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple) +{ + return LIST_FIND(&helpers, helper_cmp, + struct ip_conntrack_helper *, + tuple); +} + /* Compare parts depending on mask. */ static inline int expect_cmp(const struct ip_conntrack_expect *i, const struct ip_conntrack_tuple *tuple) { + MUST_BE_READ_LOCKED(&ip_conntrack_expect_tuple_lock); return ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask); } @@ -513,7 +596,7 @@ return ERR_PTR(-ENOMEM); } - memset(conntrack, 0, sizeof(struct ip_conntrack)); + memset(conntrack, 0, sizeof(*conntrack)); atomic_set(&conntrack->ct_general.use, 1); conntrack->ct_general.destroy = destroy_conntrack; conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple; @@ -532,31 +615,44 @@ conntrack->timeout.data = (unsigned long)conntrack; conntrack->timeout.function = death_by_timeout; + INIT_LIST_HEAD(&conntrack->sibling_list); + /* Mark clearly that it's not in the hash table. */ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list.next = NULL; - /* Write lock required for deletion of expected. Without - this, a read-lock would do. */ WRITE_LOCK(&ip_conntrack_lock); - conntrack->helper = LIST_FIND(&helpers, helper_cmp, - struct ip_conntrack_helper *, - &repl_tuple); /* Need finding and deleting of expected ONLY if we win race */ + READ_LOCK(&ip_conntrack_expect_tuple_lock); expected = LIST_FIND(&expect_list, expect_cmp, struct ip_conntrack_expect *, tuple); + READ_UNLOCK(&ip_conntrack_expect_tuple_lock); + + /* Look up the conntrack helper for master connections only */ + if (!expected) + conntrack->helper = ip_ct_find_helper(&repl_tuple); + + /* If the expectation is dying, then this is a looser. */ + if (expected + && expected->expectant->helper->timeout + && ! 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)) { + DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", + conntrack, expected); /* Welcome, Mr. Bond. We've been expecting you... */ + IP_NF_ASSERT(master_ct(conntrack)); conntrack->status = IPS_EXPECTED; - conntrack->master.master = &expected->expectant->ct_general; - IP_NF_ASSERT(conntrack->master.master); + conntrack->master = expected; + expected->sibling = conntrack; LIST_DELETE(&expect_list, expected); - expected->expectant = NULL; - nf_conntrack_get(&conntrack->master); + expected->expectant->expecting--; + nf_conntrack_get(&master_ct(conntrack)->infos[0]); } atomic_inc(&ip_conntrack_count); WRITE_UNLOCK(&ip_conntrack_lock); @@ -661,7 +757,7 @@ return NF_STOLEN; } - proto = find_proto((*pskb)->nh.iph->protocol); + proto = ip_ct_find_proto((*pskb)->nh.iph->protocol); /* It may be an icmp error... */ if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP @@ -705,66 +801,210 @@ int invert_tuplepr(struct ip_conntrack_tuple *inverse, const struct ip_conntrack_tuple *orig) { - return invert_tuple(inverse, orig, find_proto(orig->dst.protonum)); + return invert_tuple(inverse, orig, ip_ct_find_proto(orig->dst.protonum)); } -static void unexpect_related(struct ip_conntrack *related_to) -{ - MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); - list_del(&related_to->expected.list); - related_to->expected.expectant = NULL; +static inline int resent_expect(const struct ip_conntrack_expect *i, + const struct ip_conntrack_tuple *tuple, + const struct ip_conntrack_tuple *mask) +{ + DEBUGP("resent_expect\n"); + DEBUGP(" tuple: "); DUMP_TUPLE(&i->tuple); + DEBUGP("ct_tuple: "); DUMP_TUPLE(&i->ct_tuple); + DEBUGP("test tuple: "); DUMP_TUPLE(tuple); + return (((i->ct_tuple.dst.protonum == 0 && ip_ct_tuple_equal(&i->tuple, tuple)) + || (i->ct_tuple.dst.protonum && ip_ct_tuple_equal(&i->ct_tuple, tuple))) + && ip_ct_tuple_equal(&i->mask, mask)); } /* Would two expected things clash? */ static inline int expect_clash(const struct ip_conntrack_expect *i, - const struct ip_conntrack_expect *new) + const struct ip_conntrack_tuple *tuple, + const struct ip_conntrack_tuple *mask) { /* Part covered by intersection of masks must be unequal, otherwise they clash */ struct ip_conntrack_tuple intersect_mask - = { { i->mask.src.ip & new->mask.src.ip, - { i->mask.src.u.all & new->mask.src.u.all } }, - { i->mask.dst.ip & new->mask.dst.ip, - { i->mask.dst.u.all & new->mask.dst.u.all }, - i->mask.dst.protonum & new->mask.dst.protonum } }; + = { { i->mask.src.ip & mask->src.ip, + { i->mask.src.u.all & mask->src.u.all } }, + { i->mask.dst.ip & mask->dst.ip, + { i->mask.dst.u.all & mask->dst.u.all }, + i->mask.dst.protonum & mask->dst.protonum } }; + + return ip_ct_tuple_mask_cmp(&i->tuple, tuple, &intersect_mask); +} - return ip_ct_tuple_mask_cmp(&i->tuple, &new->tuple, &intersect_mask); +void ip_conntrack_unexpect_related(struct ip_conntrack_expect *expect) +{ + WRITE_LOCK(&ip_conntrack_lock); + unexpect_related(expect); + WRITE_UNLOCK(&ip_conntrack_lock); +} + +static void expectation_timed_out(unsigned long ul_expect) +{ + struct ip_conntrack_expect *expect = (void *) ul_expect; + + DEBUGP("expectation %p timed out\n", expect); + ip_conntrack_unexpect_related(expect); } /* Add a related connection. */ int ip_conntrack_expect_related(struct ip_conntrack *related_to, - const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *mask, - int (*expectfn)(struct ip_conntrack *)) + struct ip_conntrack_expect *expect) { - WRITE_LOCK(&ip_conntrack_lock); - if (related_to->expected.expectant) - unexpect_related(related_to); + struct ip_conntrack_expect *new; + int ret = 0; - related_to->expected.tuple = *tuple; - related_to->expected.mask = *mask; - related_to->expected.expectfn = expectfn; + WRITE_LOCK(&ip_conntrack_lock); + /* Because of the write lock, no reader can walk the lists, + * so there is no need to use the tuple lock too */ - if (LIST_FIND(&expect_list, expect_clash, - struct ip_conntrack_expect *, &related_to->expected)) { + DEBUGP("ip_conntrack_expect_related %p\n", related_to); + DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple); + DEBUGP("mask: "); DUMP_TUPLE(&expect->mask); + + new = LIST_FIND(&expect_list, resent_expect, + struct ip_conntrack_expect *, &expect->tuple, &expect->mask); + if (new) { + /* Helper private data may contain offsets but no pointers + pointing into the payload - otherwise we should have to copy + the data filled out by the helper over the old one */ + DEBUGP("expect_related: resent packet\n"); + if (related_to->helper->timeout) { + /* Refresh timer, if possible... */ + if (del_timer(&new->timeout)) { + new->timeout.expires = jiffies + related_to->helper->timeout * HZ; + add_timer(&new->timeout); + WRITE_UNLOCK(&ip_conntrack_lock); + return -EEXIST; + } + /* ... otherwise expectation is dying. Fall over and create a new one. */ + new = NULL; + } else { + WRITE_UNLOCK(&ip_conntrack_lock); + return -EEXIST; + } + } else if (related_to->helper->max_expected + && related_to->expecting >= related_to->helper->max_expected) { + 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) { + struct list_head *cur_item; + + /* Let's choose the the oldest expectation to overwrite */ + list_for_each(cur_item, &related_to->sibling_list) { + new = list_entry(cur_item, struct ip_conntrack_expect, + expected_list); + if (new->sibling == NULL) + break; + } + IP_NF_ASSERT(new); + if (related_to->helper->timeout + && !del_timer(&new->timeout)) { + /* Expectation is dying. Fall over and create a new one */ + new = NULL; + } else { + list_del(&new->list); + list_del(&new->expected_list); + related_to->expecting--; + ret = -EPERM; + } + } else { + WRITE_UNLOCK(&ip_conntrack_lock); + return -EPERM; + } + } else if (LIST_FIND(&expect_list, expect_clash, + struct ip_conntrack_expect *, &expect->tuple, &expect->mask)) { WRITE_UNLOCK(&ip_conntrack_lock); + DEBUGP("expect_related: busy!\n"); return -EBUSY; } + + if (!new) { + new = (struct ip_conntrack_expect *) + kmalloc(sizeof(*expect), GFP_ATOMIC); + if (!new) { + WRITE_UNLOCK(&ip_conntrack_lock); + DEBUGP("expect_relaed: OOM allocating expect\n"); + return -ENOMEM; + } + } + + /* 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; + + /* add to expected list for this connection */ + list_add(&new->expected_list, &related_to->sibling_list); + /* add to global list of expectations */ + list_prepend(&expect_list, &new->list); + /* add and start timer if required */ + if (related_to->helper->timeout) { + init_timer(&new->timeout); + new->timeout.data = (unsigned long)new; + new->timeout.function = expectation_timed_out; + new->timeout.expires = jiffies + related_to->helper->timeout * HZ; + add_timer(&new->timeout); + } + related_to->expecting++; - list_prepend(&expect_list, &related_to->expected); - related_to->expected.expectant = related_to; WRITE_UNLOCK(&ip_conntrack_lock); - return 0; + return ret; } -void ip_conntrack_unexpect_related(struct ip_conntrack *related_to) +/* Change tuple in an existing expectation */ +int ip_conntrack_change_expect(struct ip_conntrack_expect *expect, + struct ip_conntrack_tuple *newtuple) { - WRITE_LOCK(&ip_conntrack_lock); - unexpect_related(related_to); - WRITE_UNLOCK(&ip_conntrack_lock); -} + MUST_BE_READ_LOCKED(&ip_conntrack_lock); + + DEBUGP("change_expect:\n"); + DEBUGP("exp tuple: "); DUMP_TUPLE(&expect->tuple); + DEBUGP("exp mask: "); DUMP_TUPLE(&expect->mask); + DEBUGP("newtuple: "); DUMP_TUPLE(newtuple); + if (expect->ct_tuple.dst.protonum == 0) { + /* Never seen before */ + DEBUGP("change expect: never seen before\n"); + if (!ip_ct_tuple_equal(&expect->tuple, newtuple) + && LIST_FIND(&expect_list, expect_clash, + struct ip_conntrack_expect *, newtuple, &expect->mask)) { + /* Force NAT to find an unused tuple */ + return -1; + } else { + WRITE_LOCK(&ip_conntrack_expect_tuple_lock); + memcpy(&expect->ct_tuple, &expect->tuple, sizeof(expect->tuple)); + memcpy(&expect->tuple, newtuple, sizeof(expect->tuple)); + WRITE_UNLOCK(&ip_conntrack_expect_tuple_lock); + return 0; + } + } else { + /* Resent packet */ + DEBUGP("change expect: resent packet\n"); + if (ip_ct_tuple_equal(&expect->tuple, newtuple)) { + return 0; + } else { + /* Force NAT to choose again the same port */ + return -1; + } + } + return -1; +} + /* Alter reply tuple (maybe alter helper). If it's already taken, return 0 and don't do alteration. */ int ip_conntrack_alter_reply(struct ip_conntrack *conntrack, @@ -782,10 +1022,12 @@ DUMP_TUPLE(newreply); conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; - conntrack->helper = LIST_FIND(&helpers, helper_cmp, - struct ip_conntrack_helper *, - newreply); + if (!conntrack->master) + conntrack->helper = LIST_FIND(&helpers, helper_cmp, + struct ip_conntrack_helper *, + newreply); WRITE_UNLOCK(&ip_conntrack_lock); + return 1; } @@ -804,14 +1046,10 @@ const struct ip_conntrack_helper *me) { if (i->ctrack->helper == me) { - i->ctrack->helper = NULL; /* Get rid of any expected. */ - if (i->ctrack->expected.expectant) { - IP_NF_ASSERT(i->ctrack->expected.expectant - == i->ctrack); - LIST_DELETE(&expect_list, &i->ctrack->expected); - i->ctrack->expected.expectant = NULL; - } + destroy_expectations(i->ctrack); + /* And *then* set helper to NULL */ + i->ctrack->helper = NULL; } return 0; } @@ -1015,29 +1253,6 @@ SO_ORIGINAL_DST, SO_ORIGINAL_DST+1, &getorigdst, 0, NULL }; -#define NET_IP_CONNTRACK_MAX 2089 -#define NET_IP_CONNTRACK_MAX_NAME "ip_conntrack_max" - -#ifdef CONFIG_SYSCTL -static struct ctl_table_header *ip_conntrack_sysctl_header; - -static ctl_table ip_conntrack_table[] = { - { NET_IP_CONNTRACK_MAX, NET_IP_CONNTRACK_MAX_NAME, &ip_conntrack_max, - sizeof(ip_conntrack_max), 0644, NULL, proc_dointvec }, - { 0 } -}; - -static ctl_table ip_conntrack_dir_table[] = { - {NET_IPV4, "ipv4", NULL, 0, 0555, ip_conntrack_table, 0, 0, 0, 0, 0}, - { 0 } -}; - -static ctl_table ip_conntrack_root_table[] = { - {CTL_NET, "net", NULL, 0, 0555, ip_conntrack_dir_table, 0, 0, 0, 0, 0}, - { 0 } -}; -#endif /*CONFIG_SYSCTL*/ - static int kill_all(const struct ip_conntrack *i, void *data) { return 1; @@ -1047,9 +1262,6 @@ supposed to kill the mall. */ void ip_conntrack_cleanup(void) { -#ifdef CONFIG_SYSCTL - unregister_sysctl_table(ip_conntrack_sysctl_header); -#endif ip_ct_attach = NULL; /* This makes sure all current packets have passed through netfilter framework. Roll on, two-stage module @@ -1092,8 +1304,10 @@ } ip_conntrack_max = 8 * ip_conntrack_htable_size; - printk("ip_conntrack (%u buckets, %d max)\n", - ip_conntrack_htable_size, ip_conntrack_max); + printk("ip_conntrack version %s (%u buckets, %d max)" + " - %d bytes per conntrack\n", IP_CONNTRACK_VERSION, + ip_conntrack_htable_size, ip_conntrack_max, + sizeof(struct ip_conntrack)); ret = nf_register_sockopt(&so_getorigdst); if (ret != 0) @@ -1127,20 +1341,6 @@ for (i = 0; i < ip_conntrack_htable_size; i++) INIT_LIST_HEAD(&ip_conntrack_hash[i]); -/* This is fucking braindead. There is NO WAY of doing this without - the CONFIG_SYSCTL unless you don't want to detect errors. - Grrr... --RR */ -#ifdef CONFIG_SYSCTL - ip_conntrack_sysctl_header - = register_sysctl_table(ip_conntrack_root_table, 0); - if (ip_conntrack_sysctl_header == NULL) { - kmem_cache_destroy(ip_conntrack_cachep); - vfree(ip_conntrack_hash); - nf_unregister_sockopt(&so_getorigdst); - return -ENOMEM; - } -#endif /*CONFIG_SYSCTL*/ - /* For use by ipt_REJECT */ ip_ct_attach = ip_conntrack_attach; return ret; diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_egg.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_egg.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_egg.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_egg.c 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,234 @@ +/* Eggdrop extension for IP connection tracking, Version 0.0.4 + * based on ip_conntrack_irc.c + * + * This module only supports the share userfile-send command, + * used by eggdrops to share it's userfile. + * + * There are no support for NAT at the moment. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Module load syntax: + * + * please give the ports of all Eggdrops You have running + * on your system, the default port is 3333. + * + * 2001-04-19: Security update. IP addresses are now compared + * to prevent unauthorized "related" access. + * + * 2002-03-25: Harald Welte : + * Port to netfilter 'newnat' API. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_c = 0; +static unsigned int egg_timeout = 300; + +MODULE_AUTHOR("Magnus Sandin "); +MODULE_DESCRIPTION("Eggdrop (userfile-sharing) connection tracking module"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of eggdrop servers"); +#endif + +DECLARE_LOCK(ip_egg_lock); +struct module *ip_conntrack_egg = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +int parse_command(char *data, char *data_end, u_int32_t * ip, u_int16_t * port) +/* tries to get the ip_addr and port out of a eggdrop command + return value: -1 on failure, 0 on success + data pointer to first byte of DCC command data + data_end pointer to last byte of dcc command data + ip returns parsed ip of dcc command + port returns parsed port of dcc command */ +{ + if (data > data_end) + return -1; + + *ip = simple_strtoul(data, &data, 10); + + /* skip blanks between ip and port */ + while (*data == ' ' && data < data_end) + data++; + + *port = simple_strtoul(data, &data, 10); + return 0; +} + + +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + /* tcplen not negative guarenteed by ip_conntrack_tcp.c */ + struct tcphdr *tcph = (void *) iph + iph->ihl * 4; + char *data = (char *) tcph + tcph->doff * 4; + char *data_limit; + u_int32_t tcplen = len - iph->ihl * 4; + u_int32_t datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + int bytes_scanned = 0; + struct ip_conntrack_expect exp; + + u_int32_t egg_ip; + u_int16_t egg_port; + + DEBUGP("entered\n"); + + /* If packet is coming from IRC server */ + if (dir != IP_CT_DIR_REPLY) + return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { + DEBUGP("tcplen = %u\n", (unsigned) tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *) tcph, tcplen, 0))) { + DEBUGP("bad csum: %p %u %u.%u.%u.%u -> %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + data_limit = (char *) data + datalen; + while (datalen > 5 && bytes_scanned < 128) { + if (memcmp(data, "s us ", 5)) { + data++; + datalen--; + bytes_scanned++; + continue; + } + + data += 5; + + DEBUGP("Userfile-share found in connection " + "%u.%u.%u.%u -> %u.%u.%u.%u\n", + NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); + + if (parse_command((char *) data, data_limit, &egg_ip, + &egg_port)) { + DEBUGP("no data in userfile-share pkt\n"); + return NF_ACCEPT; + } + + memset(&exp, 0, sizeof(exp)); + + if (ct->tuplehash[dir].tuple.src.ip != htonl(egg_ip)) { + if (net_ratelimit()) + printk("Forged Eggdrop command from " + "%u.%u.%u.%u: %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + HIPQUAD(egg_ip), egg_port); + return NF_ACCEPT; + } + + exp.tuple.src.ip = iph->daddr; + exp.tuple.src.u.tcp.port = 0; + exp.tuple.dst.ip = htonl(egg_ip); + exp.tuple.dst.u.tcp.port = htons(egg_port); + exp.tuple.dst.protonum = IPPROTO_TCP; + + exp.mask.dst.u.tcp.port = 0xffff; + exp.mask.dst.protonum = 0xffff; + + DEBUGP("expect_related %u.%u.%u.%u:%u - %u.%u.%u.%u:%u\n", + NIPQUAD(t.src.ip), ntohs(t.src.u.tcp.port), + NIPQUAD(t.dst.ip), ntohs(t.dst.u.tcp.port)); + + ip_conntrack_expect_related(ct, &exp); + break; + } + return NF_ACCEPT; +} + +static struct ip_conntrack_helper egg_helpers[MAX_PORTS]; +static char egg_names[MAX_PORTS][14]; /* eggdrop-65535 */ + +static void __exit fini(void); + +static int __init init(void) +{ + int i, ret; + char *tmpname; + + /* If no port given, default to standard eggdrop port */ + if (ports[0] == 0) + ports[0] = 3333; + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + memset(&egg_helpers[i], 0, + sizeof(struct ip_conntrack_helper)); + egg_helpers[i].tuple.src.u.tcp.port = htons(ports[i]); + egg_helpers[i].tuple.dst.protonum = IPPROTO_TCP; + egg_helpers[i].mask.src.u.tcp.port = 0xFFFF; + egg_helpers[i].mask.dst.protonum = 0xFFFF; + egg_helpers[i].max_expected = 1; + egg_helpers[i].timeout = egg_timeout; + egg_helpers[i].flags = IP_CT_HELPER_F_REUSE_EXPECT; + egg_helpers[i].me = THIS_MODULE; + egg_helpers[i].help = help; + + tmpname = &egg_names[i][0]; + if (ports[i] == 3333) + sprintf(tmpname, "eggdrop"); + else + sprintf(tmpname, "eggdrop-%d", ports[i]); + egg_helpers[i].name = tmpname; + + DEBUGP("port #%d: %d\n", i, ports[i]); + + ret = ip_conntrack_helper_register(&egg_helpers[i]); + + if (ret) { + printk("ip_conntrack_egg: ERROR registering helper " + "for port %d\n", ports[i]); + fini(); + return 1; + } + ports_c++; + } + return 0; +} + +static void __exit fini(void) +{ + int i; + for (i = 0; i < ports_c; i++) { + DEBUGP("unregistering helper for port %d\n", ports[i]); + ip_conntrack_helper_unregister(&egg_helpers[i]); + } +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_ftp.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_ftp.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_ftp.c 2001-10-31 01:08:12.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_ftp.c 2002-07-07 00:19:39.000000000 +0200 @@ -1,4 +1,5 @@ /* FTP extension for IP connection tracking. */ +#include #include #include #include @@ -15,7 +16,7 @@ #define MAX_PORTS 8 static int ports[MAX_PORTS]; -static int ports_c; +static int ports_c = 0; #ifdef MODULE_PARM MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); #endif @@ -242,8 +243,10 @@ u_int32_t array[6] = { 0 }; int dir = CTINFO2DIR(ctinfo); unsigned int matchlen, matchoff; - struct ip_conntrack_tuple t, mask; - struct ip_ct_ftp *info = &ct->help.ct_ftp_info; + struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_ftp_expect *exp_ftp_info = &exp->help.exp_ftp_info; + unsigned int i; int found = 0; @@ -271,8 +274,8 @@ } LOCK_BH(&ip_ftp_lock); - old_seq_aft_nl_set = info->seq_aft_nl_set[dir]; - old_seq_aft_nl = info->seq_aft_nl[dir]; + old_seq_aft_nl_set = ct_ftp_info->seq_aft_nl_set[dir]; + old_seq_aft_nl = ct_ftp_info->seq_aft_nl[dir]; DEBUGP("conntrack_ftp: datalen %u\n", datalen); if ((datalen > 0) && (data[datalen-1] == '\n')) { @@ -281,8 +284,9 @@ || after(ntohl(tcph->seq) + datalen, old_seq_aft_nl)) { DEBUGP("conntrack_ftp: updating nl to %u\n", ntohl(tcph->seq) + datalen); - info->seq_aft_nl[dir] = ntohl(tcph->seq) + datalen; - info->seq_aft_nl_set[dir] = 1; + ct_ftp_info->seq_aft_nl[dir] = + ntohl(tcph->seq) + datalen; + ct_ftp_info->seq_aft_nl_set[dir] = 1; } } UNLOCK_BH(&ip_ftp_lock); @@ -330,16 +334,17 @@ DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n", (int)matchlen, data + matchoff, matchlen, ntohl(tcph->seq) + matchoff); + + memset(&expect, 0, sizeof(expect)); /* Update the ftp info */ LOCK_BH(&ip_ftp_lock); if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]) == ct->tuplehash[dir].tuple.src.ip) { - info->is_ftp = 21; - info->seq = ntohl(tcph->seq) + matchoff; - info->len = matchlen; - info->ftptype = search[i].ftptype; - info->port = array[4] << 8 | array[5]; + exp->seq = ntohl(tcph->seq) + matchoff; + exp_ftp_info->len = matchlen; + exp_ftp_info->ftptype = search[i].ftptype; + exp_ftp_info->port = array[4] << 8 | array[5]; } else { /* Enrico Scholz's passive FTP to partially RNAT'd ftp server: it really wants us to connect to a @@ -356,18 +361,21 @@ if (!loose) goto out; } - t = ((struct ip_conntrack_tuple) + exp->tuple = ((struct ip_conntrack_tuple) { { ct->tuplehash[!dir].tuple.src.ip, { 0 } }, { htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]), { htons(array[4] << 8 | array[5]) }, IPPROTO_TCP }}); - mask = ((struct ip_conntrack_tuple) + exp->mask = ((struct ip_conntrack_tuple) { { 0xFFFFFFFF, { 0 } }, { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; + /* Ignore failure; should only happen with NAT */ - ip_conntrack_expect_related(ct, &t, &mask, NULL); + ip_conntrack_expect_related(ct, &expect); out: UNLOCK_BH(&ip_ftp_lock); @@ -375,12 +383,13 @@ } static struct ip_conntrack_helper ftp[MAX_PORTS]; +static char ftp_names[MAX_PORTS][10]; /* Not __exit: called from init() */ static void fini(void) { int i; - for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + for (i = 0; i < ports_c; i++) { DEBUGP("ip_ct_ftp: unregistering helper for port %d\n", ports[i]); ip_conntrack_helper_unregister(&ftp[i]); @@ -390,9 +399,10 @@ static int __init init(void) { int i, ret; + char *tmpname; if (ports[0] == 0) - ports[0] = 21; + ports[0] = FTP_PORT; for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { memset(&ftp[i], 0, sizeof(struct ip_conntrack_helper)); @@ -400,7 +410,19 @@ ftp[i].tuple.dst.protonum = IPPROTO_TCP; ftp[i].mask.src.u.tcp.port = 0xFFFF; ftp[i].mask.dst.protonum = 0xFFFF; + ftp[i].max_expected = 1; + ftp[i].timeout = 0; + ftp[i].flags = IP_CT_HELPER_F_REUSE_EXPECT; + ftp[i].me = ip_conntrack_ftp; ftp[i].help = help; + + tmpname = &ftp_names[i][0]; + if (ports[i] == FTP_PORT) + sprintf(tmpname, "ftp"); + else + sprintf(tmpname, "ftp-%d", ports[i]); + ftp[i].name = tmpname; + DEBUGP("ip_ct_ftp: registering helper for port %d\n", ports[i]); ret = ip_conntrack_helper_register(&ftp[i]); @@ -414,10 +436,10 @@ return 0; } - +#ifdef CONFIG_IP_NF_NAT_NEEDED EXPORT_SYMBOL(ip_ftp_lock); -EXPORT_SYMBOL(ip_conntrack_ftp); -MODULE_LICENSE("GPL"); +#endif +MODULE_LICENSE("GPL"); module_init(init); module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_h323.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_h323.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_h323.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_h323.c 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,310 @@ +/* + * H.323 'brute force' extension for H.323 connection tracking. + * Jozsef Kadlecsik + * + * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project. + * (http://www.coritel.it/projects/sofia/nat/) + * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind' + * the unregistered helpers to the conntrack entries. + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("H.323 'brute force' connection tracking module"); +MODULE_LICENSE("GPL"); + +DECLARE_LOCK(ip_h323_lock); +struct module *ip_conntrack_h323 = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/* FIXME: This should be in userspace. Later. */ +static int h245_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + unsigned char *data = (unsigned char *) tcph + tcph->doff * 4; + unsigned char *data_limit; + u_int32_t tcplen = len - iph->ihl * 4; + u_int32_t datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info; + u_int16_t data_port; + u_int32_t data_ip; + unsigned int i; + + DEBUGP("ct_h245_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), + NIPQUAD(iph->daddr), ntohs(tcph->dest)); + + /* Can't track connections formed before we registered */ + if (!info) + return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ct_h245_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header or too short packet? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) { + DEBUGP("ct_h245_help: tcplen = %u\n", (unsigned)tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcplen, 0))) { + DEBUGP("ct_h245_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + data_limit = (unsigned char *) data + datalen; + /* bytes: 0123 45 + ipadrr port */ + for (i = 0; data < (data_limit - 5); data++, i++) { + data_ip = *((u_int32_t *)data); + if (data_ip == iph->saddr) { + data_port = *((u_int16_t *)(data + 4)); + memset(&expect, 0, sizeof(expect)); + /* update the H.225 info */ + DEBUGP("ct_h245_help: new RTCP/RTP requested %u.%u.%u.%u:->%u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), + NIPQUAD(iph->saddr), ntohs(data_port)); + LOCK_BH(&ip_h323_lock); + info->is_h225 = H225_PORT + 1; + exp_info->port = data_port; + exp_info->dir = dir; + exp_info->offset = i; + + exp->seq = ntohl(tcph->seq) + i; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { data_ip, + { data_port }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, exp); + + UNLOCK_BH(&ip_h323_lock); + } + } + + return NF_ACCEPT; + +} + +/* H.245 helper is not registered! */ +static struct ip_conntrack_helper h245 = + { { NULL, NULL }, + "H.245", /* name */ + IP_CT_HELPER_F_REUSE_EXPECT, /* flags */ + NULL, /* module */ + 8, /* max_ expected */ + 240, /* timeout */ + { { 0, { 0 } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h245_help /* helper */ + }; + +static int h225_expect(struct ip_conntrack *ct) +{ + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &h245; + DEBUGP("h225_expect: helper for %p added\n", ct); + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +/* FIXME: This should be in userspace. Later. */ +static int h225_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + unsigned char *data = (unsigned char *) tcph + tcph->doff * 4; + unsigned char *data_limit; + u_int32_t tcplen = len - iph->ihl * 4; + u_int32_t datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info; + u_int16_t data_port; + u_int32_t data_ip; + unsigned int i; + + DEBUGP("ct_h225_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), + NIPQUAD(iph->daddr), ntohs(tcph->dest)); + + /* Can't track connections formed before we registered */ + if (!info) + return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ct_h225_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header or too short packet? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) { + DEBUGP("ct_h225_help: tcplen = %u\n", (unsigned)tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcplen, 0))) { + DEBUGP("ct_h225_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + data_limit = (unsigned char *) data + datalen; + /* bytes: 0123 45 + ipadrr port */ + for (i = 0; data < (data_limit - 5); data++, i++) { + data_ip = *((u_int32_t *)data); + if (data_ip == iph->saddr) { + data_port = *((u_int16_t *)(data + 4)); + if (data_port == tcph->source) { + /* Signal address */ + DEBUGP("ct_h225_help: sourceCallSignalAddress from %u.%u.%u.%u\n", + NIPQUAD(iph->saddr)); + /* Update the H.225 info so that NAT can mangle the address/port + even when we have no expected connection! */ +#ifdef CONFIG_IP_NF_NAT_NEEDED + LOCK_BH(&ip_h323_lock); + info->dir = dir; + info->seq[IP_CT_DIR_ORIGINAL] = ntohl(tcph->seq) + i; + info->offset[IP_CT_DIR_ORIGINAL] = i; + UNLOCK_BH(&ip_h323_lock); +#endif + } else { + memset(&expect, 0, sizeof(expect)); + + /* update the H.225 info */ + LOCK_BH(&ip_h323_lock); + info->is_h225 = H225_PORT; + exp_info->port = data_port; + exp_info->dir = dir; + exp_info->offset = i; + + exp->seq = ntohl(tcph->seq) + i; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { data_ip, + { data_port }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = h225_expect; + + /* Ignore failure */ + ip_conntrack_expect_related(ct, exp); + + DEBUGP("ct_h225_help: new H.245 requested %u.%u.%u.%u->%u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), + NIPQUAD(iph->saddr), ntohs(data_port)); + + UNLOCK_BH(&ip_h323_lock); + } +#ifdef CONFIG_IP_NF_NAT_NEEDED + } else if (data_ip == iph->daddr) { + data_port = *((u_int16_t *)(data + 4)); + if (data_port == tcph->dest) { + /* Signal address */ + DEBUGP("ct_h225_help: destCallSignalAddress %u.%u.%u.%u\n", + NIPQUAD(iph->daddr)); + /* Update the H.225 info so that NAT can mangle the address/port + even when we have no expected connection! */ + LOCK_BH(&ip_h323_lock); + info->dir = dir; + info->seq[IP_CT_DIR_REPLY] = ntohl(tcph->seq) + i; + info->offset[IP_CT_DIR_REPLY] = i; + UNLOCK_BH(&ip_h323_lock); + } +#endif + } + } + + return NF_ACCEPT; + +} + +static struct ip_conntrack_helper h225 = + { { NULL, NULL }, + "H.225", /* name */ + IP_CT_HELPER_F_REUSE_EXPECT, /* flags */ + THIS_MODULE, /* module */ + 2, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(H225_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h225_help /* helper */ + }; + +static int __init init(void) +{ + return ip_conntrack_helper_register(&h225); +} + +static void __exit fini(void) +{ + /* Unregister H.225 helper */ + ip_conntrack_helper_unregister(&h225); +} + +#ifdef CONFIG_IP_NF_NAT_NEEDED +EXPORT_SYMBOL(ip_h323_lock); +#endif + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_irc.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_irc.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_irc.c 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_irc.c 2002-07-07 00:19:39.000000000 +0200 @@ -11,12 +11,18 @@ ** * Module load syntax: * insmod ip_conntrack_irc.o ports=port1,port2,...port + * max_dcc_channels=n dcc_timeout=secs * * please give the ports of all IRC servers You wish to connect to. - * If You don't specify ports, the default will be port 6667 + * If You don't specify ports, the default will be port 6667. + * With max_dcc_channels you can define the maximum number of not + * yet answered DCC channels per IRC session (default 8). + * With dcc_timeout you can specify how long the system waits for + * an expected DCC channel (default 300 seconds). * */ +#include #include #include #include @@ -29,7 +35,9 @@ #define MAX_PORTS 8 static int ports[MAX_PORTS]; -static int ports_n_c = 0; +static int ports_c = 0; +static int max_dcc_channels = 8; +static unsigned int dcc_timeout = 300; MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("IRC (DCC) connection tracking module"); @@ -37,6 +45,10 @@ #ifdef MODULE_PARM MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); MODULE_PARM_DESC(ports, "port numbers of IRC servers"); +MODULE_PARM(max_dcc_channels, "i"); +MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per IRC session"); +MODULE_PARM(dcc_timeout, "i"); +MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels"); #endif #define NUM_DCCPROTO 5 @@ -103,23 +115,15 @@ u_int32_t tcplen = len - iph->ihl * 4; u_int32_t datalen = tcplen - tcph->doff * 4; int dir = CTINFO2DIR(ctinfo); - struct ip_conntrack_tuple t, mask; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_irc_expect *exp_irc_info = &exp->help.exp_irc_info; u_int32_t dcc_ip; u_int16_t dcc_port; int i; char *addr_beg_p, *addr_end_p; - struct ip_ct_irc *info = &ct->help.ct_irc_info; - - mask = ((struct ip_conntrack_tuple) - { { 0, { 0 } }, - { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); - DEBUGP("entered\n"); - /* Can't track connections formed before we registered */ - if (!info) - return NF_ACCEPT; /* If packet is coming from IRC server */ if (dir == IP_CT_DIR_REPLY) @@ -189,33 +193,37 @@ continue; } + + memset(&expect, 0, sizeof(expect)); LOCK_BH(&ip_irc_lock); /* save position of address in dcc string, * neccessary for NAT */ - info->is_irc = IP_CONNTR_IRC; DEBUGP("tcph->seq = %u\n", tcph->seq); - info->seq = ntohl(tcph->seq) + (addr_beg_p - _data); - info->len = (addr_end_p - addr_beg_p); - info->port = dcc_port; + exp->seq = ntohl(tcph->seq) + (addr_beg_p - _data); + exp_irc_info->len = (addr_end_p - addr_beg_p); + exp_irc_info->port = dcc_port; DEBUGP("wrote info seq=%u (ofs=%u), len=%d\n", - info->seq, (addr_end_p - _data), info->len); + exp->seq, (addr_end_p - _data), exp_irc_info->len); - memset(&t, 0, sizeof(t)); - t.src.ip = 0; - t.src.u.tcp.port = 0; - t.dst.ip = htonl(dcc_ip); - t.dst.u.tcp.port = htons(info->port); - t.dst.protonum = IPPROTO_TCP; + exp->tuple = ((struct ip_conntrack_tuple) + { { 0, { 0 } }, + { htonl(dcc_ip), { htons(dcc_port) }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", - NIPQUAD(t.src.ip), - ntohs(t.src.u.tcp.port), - NIPQUAD(t.dst.ip), - ntohs(t.dst.u.tcp.port)); + NIPQUAD(exp->tuple.src.ip), + ntohs(exp->tuple.src.u.tcp.port), + NIPQUAD(exp->tuple.dst.ip), + ntohs(exp->tuple.dst.u.tcp.port)); - ip_conntrack_expect_related(ct, &t, &mask, NULL); + ip_conntrack_expect_related(ct, &expect); UNLOCK_BH(&ip_irc_lock); return NF_ACCEPT; @@ -226,29 +234,53 @@ } static struct ip_conntrack_helper irc_helpers[MAX_PORTS]; +static char irc_names[MAX_PORTS][10]; static void fini(void); static int __init init(void) { int i, ret; + struct ip_conntrack_helper *hlpr; + char *tmpname; + if (max_dcc_channels < 1) { + printk("ip_conntrack_irc: max_dcc_channels must be a positive integer\n"); + return -EBUSY; + } + if (dcc_timeout < 0) { + printk("ip_conntrack_irc: dcc_timeout must be a positive integer\n"); + return -EBUSY; + } + /* If no port given, default to standard irc port */ if (ports[0] == 0) - ports[0] = 6667; + ports[0] = IRC_PORT; for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { - memset(&irc_helpers[i], 0, + hlpr = &irc_helpers[i]; + memset(hlpr, 0, sizeof(struct ip_conntrack_helper)); - irc_helpers[i].tuple.src.u.tcp.port = htons(ports[i]); - irc_helpers[i].tuple.dst.protonum = IPPROTO_TCP; - irc_helpers[i].mask.src.u.tcp.port = 0xFFFF; - irc_helpers[i].mask.dst.protonum = 0xFFFF; - irc_helpers[i].help = help; + hlpr->tuple.src.u.tcp.port = htons(ports[i]); + hlpr->tuple.dst.protonum = IPPROTO_TCP; + hlpr->mask.src.u.tcp.port = 0xFFFF; + hlpr->mask.dst.protonum = 0xFFFF; + hlpr->max_expected = max_dcc_channels; + hlpr->timeout = dcc_timeout; + hlpr->flags = IP_CT_HELPER_F_REUSE_EXPECT; + hlpr->me = ip_conntrack_irc; + hlpr->help = help; + + tmpname = &irc_names[i][0]; + if (ports[i] == IRC_PORT) + sprintf(tmpname, "irc"); + else + sprintf(tmpname, "irc-%d", i); + hlpr->name = tmpname; DEBUGP("port #%d: %d\n", i, ports[i]); - ret = ip_conntrack_helper_register(&irc_helpers[i]); + ret = ip_conntrack_helper_register(hlpr); if (ret) { printk("ip_conntrack_irc: ERROR registering port %d\n", @@ -256,7 +288,7 @@ fini(); return -EBUSY; } - ports_n_c++; + ports_c++; } return 0; } @@ -266,12 +298,16 @@ static void fini(void) { int i; - for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + for (i = 0; i < ports_c; i++) { DEBUGP("unregistering port %d\n", ports[i]); ip_conntrack_helper_unregister(&irc_helpers[i]); } } +#ifdef CONFIG_IP_NF_NAT_NEEDED +EXPORT_SYMBOL(ip_irc_lock); +#endif + module_init(init); module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_proto_generic.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_proto_generic.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_proto_generic.c 2001-04-27 23:15:01.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_proto_generic.c 2002-07-07 00:19:39.000000000 +0200 @@ -4,7 +4,7 @@ #include #include -#define GENERIC_TIMEOUT (600*HZ) +unsigned long ip_ct_generic_timeout = 600*HZ; static int generic_pkt_to_tuple(const void *datah, size_t datalen, struct ip_conntrack_tuple *tuple) @@ -43,7 +43,7 @@ struct iphdr *iph, size_t len, enum ip_conntrack_info conntrackinfo) { - ip_ct_refresh(conntrack, GENERIC_TIMEOUT); + ip_ct_refresh(conntrack, ip_ct_generic_timeout); return NF_ACCEPT; } @@ -57,5 +57,5 @@ struct ip_conntrack_protocol ip_conntrack_generic_protocol = { { NULL, NULL }, 0, "unknown", generic_pkt_to_tuple, generic_invert_tuple, generic_print_tuple, - generic_print_conntrack, established, new, NULL }; + generic_print_conntrack, established, new, NULL, NULL, NULL }; diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_proto_icmp.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_proto_icmp.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2001-04-27 23:15:01.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2002-07-07 00:19:39.000000000 +0200 @@ -6,7 +6,7 @@ #include #include -#define ICMP_TIMEOUT (30*HZ) +unsigned long ip_ct_icmp_timeout = 30*HZ; #if 0 #define DEBUGP printk @@ -82,7 +82,7 @@ ct->timeout.function((unsigned long)ct); } else { atomic_inc(&ct->proto.icmp.count); - ip_ct_refresh(ct, ICMP_TIMEOUT); + ip_ct_refresh(ct, ip_ct_icmp_timeout); } return NF_ACCEPT; @@ -113,4 +113,4 @@ struct ip_conntrack_protocol ip_conntrack_protocol_icmp = { { NULL, NULL }, IPPROTO_ICMP, "icmp", icmp_pkt_to_tuple, icmp_invert_tuple, icmp_print_tuple, - icmp_print_conntrack, icmp_packet, icmp_new, NULL }; + icmp_print_conntrack, icmp_packet, icmp_new, NULL, NULL, NULL }; diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_proto_tcp.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_proto_tcp.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_proto_tcp.c 2001-04-27 23:15:01.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_proto_tcp.c 2002-07-07 02:36:43.000000000 +0200 @@ -1,3 +1,14 @@ +/* + * TCP connection tracking + */ + +/* + * Changes: + * Jozsef Kadlecsik: Real stateful connection tracking + * Modified state transitions table + * Window scaling support + */ + #define __NO_VERSION__ #include #include @@ -7,12 +18,17 @@ #include #include #include + +#include +#include + #include #include #include #if 0 #define DEBUGP printk +#define DDEBUGP #else #define DEBUGP(format, args...) #endif @@ -20,6 +36,14 @@ /* Protects conntrack->proto.tcp */ static DECLARE_RWLOCK(tcp_lock); +/* Logging options */ +int ip_ct_tcp_log_invalid_scale = 0; +int ip_ct_tcp_log_out_of_window = 0; + +/* "Be conservative in what you do, + be liberal in what you accept from others." */ +int ip_ct_tcp_be_liberal = 0; + /* FIXME: Examine ipfilter's timeouts and conntrack transitions more closely. They're more complex. --RR */ @@ -30,66 +54,202 @@ static const char *tcp_conntrack_names[] = { "NONE", - "ESTABLISHED", "SYN_SENT", "SYN_RECV", + "ESTABLISHED", "FIN_WAIT", - "TIME_WAIT", - "CLOSE", "CLOSE_WAIT", "LAST_ACK", + "TIME_WAIT", + "CLOSE", "LISTEN" }; -#define SECS *HZ +#define SECS * HZ #define MINS * 60 SECS #define HOURS * 60 MINS #define DAYS * 24 HOURS - -static unsigned long tcp_timeouts[] -= { 30 MINS, /* TCP_CONNTRACK_NONE, */ - 5 DAYS, /* TCP_CONNTRACK_ESTABLISHED, */ - 2 MINS, /* TCP_CONNTRACK_SYN_SENT, */ - 60 SECS, /* TCP_CONNTRACK_SYN_RECV, */ - 2 MINS, /* TCP_CONNTRACK_FIN_WAIT, */ - 2 MINS, /* TCP_CONNTRACK_TIME_WAIT, */ - 10 SECS, /* TCP_CONNTRACK_CLOSE, */ - 60 SECS, /* TCP_CONNTRACK_CLOSE_WAIT, */ - 30 SECS, /* TCP_CONNTRACK_LAST_ACK, */ - 2 MINS, /* TCP_CONNTRACK_LISTEN, */ +unsigned long ip_ct_tcp_timeout_none = 30 MINS; +unsigned long ip_ct_tcp_timeout_syn_sent = 2 MINS; +unsigned long ip_ct_tcp_timeout_syn_recv = 60 SECS; +unsigned long ip_ct_tcp_timeout_established = 5 DAYS; +unsigned long ip_ct_tcp_timeout_fin_wait = 2 MINS; +unsigned long ip_ct_tcp_timeout_close_wait = 12 HOURS; +unsigned long ip_ct_tcp_timeout_last_ack = 30 SECS; +unsigned long ip_ct_tcp_timeout_time_wait = 2 MINS; +unsigned long ip_ct_tcp_timeout_close = 10 SECS; +unsigned long ip_ct_tcp_timeout_listen = 2 MINS; + +static unsigned long * tcp_timeouts[] += { &ip_ct_tcp_timeout_none, /* TCP_CONNTRACK_NONE, */ + &ip_ct_tcp_timeout_syn_sent, /* TCP_CONNTRACK_SYN_SENT, */ + &ip_ct_tcp_timeout_syn_recv, /* TCP_CONNTRACK_SYN_RECV, */ + &ip_ct_tcp_timeout_established, /* TCP_CONNTRACK_ESTABLISHED, */ + &ip_ct_tcp_timeout_fin_wait, /* TCP_CONNTRACK_FIN_WAIT, */ + &ip_ct_tcp_timeout_close_wait, /* TCP_CONNTRACK_CLOSE_WAIT, */ + &ip_ct_tcp_timeout_last_ack, /* TCP_CONNTRACK_LAST_ACK, */ + &ip_ct_tcp_timeout_time_wait, /* TCP_CONNTRACK_TIME_WAIT, */ + &ip_ct_tcp_timeout_close, /* TCP_CONNTRACK_CLOSE, */ + &ip_ct_tcp_timeout_listen, /* TCP_CONNTRACK_LISTEN, */ }; #define sNO TCP_CONNTRACK_NONE -#define sES TCP_CONNTRACK_ESTABLISHED #define sSS TCP_CONNTRACK_SYN_SENT #define sSR TCP_CONNTRACK_SYN_RECV +#define sES TCP_CONNTRACK_ESTABLISHED #define sFW TCP_CONNTRACK_FIN_WAIT -#define sTW TCP_CONNTRACK_TIME_WAIT -#define sCL TCP_CONNTRACK_CLOSE #define sCW TCP_CONNTRACK_CLOSE_WAIT #define sLA TCP_CONNTRACK_LAST_ACK +#define sTW TCP_CONNTRACK_TIME_WAIT +#define sCL TCP_CONNTRACK_CLOSE #define sLI TCP_CONNTRACK_LISTEN #define sIV TCP_CONNTRACK_MAX -static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = { +/* + * The TCP state transition table needs a few words... + * + * We are the man in the middle. All the packets go through us + * but might get lost in transit to the destination. + * It is assumed that the destinations can't receive segments + * we haven't seen. + * + * The checked segment is in window. + * + * The meaning of the states are: + * + * NONE: initial state + * SYN_SENT: SYN-only packet seen + * SYN_RECV: SYN-ACK packet seen + * ESTABLISHED: ACK packet seen + * FIN_WAIT: FIN packet seen + * CLOSE_WAIT: ACK seen (after FIN) + * LAST_ACK: FIN seen (after FIN) + * TIME_WAIT: last ACK seen + * CLOSE: closed connection + * + * LISTEN state is not used. + * + */ +static enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = { { -/* ORIGINAL */ -/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI */ -/*syn*/ {sSS, sES, sSS, sSR, sSS, sSS, sSS, sSS, sSS, sLI }, -/*fin*/ {sTW, sFW, sSS, sTW, sFW, sTW, sCL, sTW, sLA, sLI }, -/*ack*/ {sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sLA, sES }, -/*rst*/ {sCL, sCL, sSS, sCL, sCL, sTW, sCL, sCL, sCL, sCL }, -/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } +/* ORIGINAL */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*syn*/ { sSS, sSS, sCL, sCL, sCL, sCL, sCL, sSS, sSS, sIV }, +/* + * sNO -> sSS Initialize a new connection + * sSS -> sSS Retransmitted SYN + * sSR -> sCL Error: SYNs in window outside the SYN_SENT state + * are errors. Receiver will either go back to the + * LISTEN state or reply with RST. + * sES -> sCL + * sFW -> sCL + * sCW -> sCL + * sLA -> sCL + * sTW -> sSS Reopened connection (RFC 1122). + * sCL -> sSS + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*synack*/ { sSR, sSR, sES, sCL, sCL, sCL, sCL, sCL, sCL, sIV }, +/* + * sNO -> sSR Assumed: hey, we've just started up! + * sSS -> sSR Simultaneous open. + * sSR -> sES Ditto. + * sES -> sCL Error. + * sFW -> sCL + * sCW -> sCL + * sLA -> sCL + * sTW -> sCL + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*fin*/ { sTW, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV }, +/* + * sNO -> sTW We assume TIME-WAIT state. + * sSS -> sIV Client migth not send FIN in this state. + * sSR -> sFW Close started. + * sES -> sFW + * sFW -> sLA FIN seen in both directions, waiting for + * the last ACK. + * Migth be a retransmitted FIN as well... + * sCW -> sLA + * sLA -> sLA Retransmitted FIN. Remain in the same state. + * sTW -> sTW + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*ack*/ { sES, sIV, sES, sES, sCW, sCW, sTW, sTW, sCL, sIV }, +/* + * sNO -> sES Assumed. + * sSS -> sIV ACK is invalid: we haven't seen a SYN/ACK yet. + * sSR -> sES Established state is reached. + * sES -> sES :-) + * sFW -> sCW Normal close request answered by ACK. + * sCW -> sCW + * sLA -> sTW Last ACK detected. + * sTW -> sTW Retransmitted last ACK. Remain in the same state. + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*rst*/ { sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV }, +/*none*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } }, { -/* REPLY */ -/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI */ -/*syn*/ {sSR, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR }, -/*fin*/ {sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI }, -/*ack*/ {sCL, sES, sSS, sSR, sFW, sTW, sCL, sCW, sCL, sLI }, -/*rst*/ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sLA, sLI }, -/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } +/* REPLY */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*syn*/ { sIV, sSS, sSR, sCL, sCL, sCL, sCL, sSS, sSS, sIV }, +/* + * sNO -> sIV Never reached. + * sSS -> sSS Simultaneous open. + * sSR -> sSR Simultaneous open, retransmitted SYN. + * We have seen a SYN/ACK, but it seems + * it is delayed or got lost. + * sES -> sCL Error. + * sFW -> sCL + * sCW -> sCL + * sLA -> sCL + * sTW -> sSS Reopened connection. + * sCL -> sSS + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*synack*/ { sIV, sSR, sES, sCL, sCL, sCL, sCL, sCL, sCL, sIV }, +/* + * sSS -> sSR Standard open. + * sSR -> sES Simultaneous open. + * sES -> sCL Error. + * sFW -> sCL + * sCW -> sCL + * sLA -> sCL + * sTW -> sCL + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*fin*/ { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV }, +/* + * sSS -> sIV Server might not send FIN in this state. + * sSR -> sFW Close started. + * sES -> sFW + * sFW -> sLA FIN seen in both directions. + * sCW -> sLA + * sLA -> sLA Retransmitted FIN. + * sTW -> sTW + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*ack*/ { sIV, sIV, sES, sES, sCW, sCW, sTW, sTW, sCL, sIV }, +/* + * sSS -> sIV ACK is invalid: we haven't seen a SYN/ACK yet. + * sSR -> sES Simultaneous open. + * sES -> sES :-) + * sFW -> sCW Normal close request answered by ACK. + * sCW -> sCW + * sLA -> sTW Last ACK detected. + * sTW -> sTW Retransmitted last ACK. + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*rst*/ { sIV, sCL, sCL, sCL, sCL, sIV, sCL, sCL, sCL, sIV }, +/*none*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } } }; @@ -136,19 +296,337 @@ static unsigned int get_conntrack_index(const struct tcphdr *tcph) { - if (tcph->rst) return 3; - else if (tcph->syn) return 0; - else if (tcph->fin) return 1; - else if (tcph->ack) return 2; - else return 4; + if (tcph->rst) return 4; + else if (tcph->syn) return (tcph->ack ? 1 : 0); + else if (tcph->fin) return 2; + else if (tcph->ack) return 3; + else return 5; +} + +/* From ipt_LOG.c... */ +/* Use lock to serialize, so printks don't overlap */ +static spinlock_t log_lock = SPIN_LOCK_UNLOCKED; + +static void log_packet(struct iphdr *iph, struct tcphdr *tcph) +{ + /* Important fields: + * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ + /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ + printk("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ", + NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); + + /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ + printk("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", + ntohs(iph->tot_len), iph->tos & IPTOS_TOS_MASK, + iph->tos & IPTOS_PREC_MASK, iph->ttl, ntohs(iph->id)); + + /* Max length: 6 "CE DF MF " */ + if (ntohs(iph->frag_off) & IP_CE) + printk("CE "); + if (ntohs(iph->frag_off) & IP_DF) + printk("DF "); + /* ... but conntrack don't see fragments */ + + if (iph->ihl * 4 != sizeof(struct iphdr)) { + unsigned int i; + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + printk("OPT ("); + for (i = sizeof(struct iphdr); i < iph->ihl * 4; i++) + printk("%02X", ((u_int8_t *)iph)[i]); + printk(") "); + } + + /* Max length: 10 "PROTO=TCP " */ + printk("PROTO=TCP "); + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + printk("SPT=%u DPT=%u ", + ntohs(tcph->source), ntohs(tcph->dest)); + /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ + printk("SEQ=%u ACK=%u ", + ntohl(tcph->seq), ntohl(tcph->ack_seq)); + /* Max length: 13 "WINDOW=65535 " */ + printk("WINDOW=%u ", ntohs(tcph->window)); + /* Max length: 9 "RES=0x3F " */ + printk("RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(tcph) & TCP_RESERVED_BITS) >> 22)); + /* Max length: 36 "URG ACK PSH RST SYN FIN " */ + if (tcph->urg) + printk("URG "); + if (tcph->ack) + printk("ACK "); + if (tcph->psh) + printk("PSH "); + if (tcph->rst) + printk("RST "); + if (tcph->syn) + printk("SYN "); + if (tcph->fin) + printk("FIN "); + /* Max length: 11 "URGP=65535 " */ + printk("URGP=%u ", ntohs(tcph->urg_ptr)); + + if (tcph->doff * 4 != sizeof(struct tcphdr)) { + unsigned int i; + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + printk("OPT ("); + for (i =sizeof(struct tcphdr); i < tcph->doff * 4; i++) + printk("%02X", ((u_int8_t *)tcph)[i]); + printk(") "); + } + + /* Proto Max log string length */ + /* IP: 40+46+6+11+127 = 230 */ + /* TCP: 10+max(25,20+30+13+9+36+11+127) = 256 */ +} + +#define log_invalid_packet(iph, tcph, format, arg...) \ +do { \ + spin_lock_bh(&log_lock); \ + log_packet(iph, tcph); \ + printk(format, ## arg); \ + spin_unlock_bh(&log_lock); \ +} while (0); + +/* TCP connection tracking based on 'Real Stateful TCP Packet Filtering + in IP Filter' by Guido van Rooij. + + http://www.nluug.nl/events/sane2000/papers.html + http://www.iae.nl/users/guido/papers/tcp_filtering.ps.gz + + The boundaries according to the article: + + td_maxend = max(ack + max(win,1)) seen in reply packets + td_maxwin = max(max(win, 1)) seen in sent packets + td_end = max(seq + len) seen in sent packets + + I. Upper bound for valid data: seq + len <= sender.td_maxend + II. Lower bound for valid data: seq >= sender.td_end - receiver.td_maxwin + III. Upper bound for valid ack: ack <= receiver.td_end + IV. Lower bound for valid ack: ack >= receiver.td_end - MAXACKWINDOW + + The upper bound limit for a valid ack is not ignored - + we doesn't have to deal with fragments. +*/ + +#define SEGMENT_SEQ_PLUS_LEN(seq, len, iph, tcph) (seq + len - (iph->ihl + tcph->doff)*4 \ + + (tcph->syn ? 1 : 0) + (tcph->fin ? 1 : 0)) + +/* Fixme: what about big packets? */ +#define MAXACKWINCONST 66000 +#define MAXACKWINDOW(receiver) ((receiver)->td_scale && (receiver)->td_maxwin < MAXACKWINCONST \ + ? (receiver)->td_maxwin : MAXACKWINCONST) + +/* + * Simplified tcp_parse_options routine from tcp_input.c + */ +static u_int8_t tcp_scale_option(struct iphdr *iph, struct tcphdr *tcph) +{ + unsigned char *ptr; + int length = (tcph->doff*4) - sizeof(struct tcphdr); + + ptr = (unsigned char *)(tcph + 1); + + while (length > 0) { + int opcode=*ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return 0; + case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ + length--; + continue; + default: + opsize=*ptr++; + if (opsize < 2) /* "silly options" */ + return 0; + if (opsize > length) + break; /* don't parse partial options */ + + if (opcode == TCPOPT_WINDOW && opsize == TCPOLEN_WINDOW) { + u_int8_t scale = *(u_int8_t *)ptr; + + if (scale > 14) { + /* See RFC1323 for an explanation of the limit to 14 */ + if (ip_ct_tcp_log_invalid_scale && net_ratelimit()) + log_invalid_packet(iph, tcph, "Illegal window scaling value %u > 14 ignored\n", + scale); + scale = 14; + } + return scale; + } + ptr += opsize - 2; + length -= opsize; + } + } + return 0; +} + +static int tcp_in_window(struct ip_ct_tcp_state *sender, + struct ip_ct_tcp_state *receiver, + struct iphdr *iph, size_t len, + struct tcphdr *tcph) +{ + __u32 seq, ack, end, swin; + __u16 win; + int res; + + /* + * Get the required data from the packet. + */ + seq = ntohl(tcph->seq); + ack = ntohl(tcph->ack_seq); + win = ntohs(tcph->window); + end = SEGMENT_SEQ_PLUS_LEN(seq, len, iph, tcph); + + DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu seq=%u ack=%u win=%u end=%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->daddr), ntohs(tcph->dest), + seq, ack, win, end); + DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i receiver end=%u maxend=%u maxwin=%u scale=%i\n", + sender->td_end, sender->td_maxend, sender->td_maxwin, sender->td_scale, + receiver->td_end, receiver->td_maxend, receiver->td_maxwin, receiver->td_scale); + + if (sender->td_end == 0) { + /* + * Initialize sender data. + */ + if (tcph->syn && tcph->ack) { + /* + * Outgoing SYN-ACK in reply to a SYN. + * + * Fixme: here we loose supporting simultaneous open... + */ + sender->td_end = + sender->td_maxend = end; + sender->td_maxwin = (win == 0 ? 1 : win); + sender->td_scale = tcp_scale_option(iph, tcph); + } else { + /* + * We are in the middle of a connection, + * its history is lost for us. + * Let's try to use the data from the packet. + */ + sender->td_end = end; + sender->td_maxwin = (win == 0 ? 1 : win); + sender->td_maxend = end + sender->td_maxwin; + sender->td_scale = 0; + } + } + + if (!(tcph->ack)) { + /* + * If there is no ACK, just pretend it was set and OK. + */ + ack = receiver->td_end; + } else if (((tcp_flag_word(tcph) & (TCP_FLAG_ACK|TCP_FLAG_RST)) == (TCP_FLAG_ACK|TCP_FLAG_RST)) + && (ack == 0)) { + /* + * Broken TCP stacks, that set ACK in RST packets as well + * with zero ack value. + */ + ack = receiver->td_end; + } + + if (seq == end) + /* + * Packets contains no data: we assume it is valid + * and check the ack value only. + */ + seq = end = sender->td_end; + + DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i receiver end=%u maxend=%u maxwin=%u scale=%i\n", + sender->td_end, sender->td_maxend, sender->td_maxwin, sender->td_scale, + receiver->td_end, receiver->td_maxend, receiver->td_maxwin, receiver->td_scale); + DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu seq=%u ack=%u win=%u end=%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->daddr), ntohs(tcph->dest), + seq, ack, win, end); + DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n", + before(end, sender->td_maxend + 1), + after(seq, sender->td_end - receiver->td_maxwin - 1), + before(ack, receiver->td_end + 1), + after(ack, receiver->td_end - MAXACKWINDOW(receiver))); + + if (before(end, sender->td_maxend + 1) && + after(seq, sender->td_end - receiver->td_maxwin - 1) && + before(ack, receiver->td_end + 1) && + after(ack, receiver->td_end - MAXACKWINDOW(receiver))) { + /* + * Take into account window scaling (RFC 1323). + */ + + swin = win; + + if (tcph->ack && sender->td_scale && receiver->td_scale) + swin <<= sender->td_scale; + /* + * Update sender data. + */ + if (sender->td_maxwin < swin) + sender->td_maxwin = swin; + if (after(end, sender->td_end)) + sender->td_end = end; + if (after(ack + swin, receiver->td_maxend - 1)) { + receiver->td_maxend = ack + swin; + if (win == 0) + receiver->td_maxend++; + } + res = 1; + } else { + if (ip_ct_tcp_log_out_of_window && net_ratelimit()) + log_invalid_packet(iph, tcph, "Out of window data: %s\n", + before(end, sender->td_maxend + 1) ? + after(seq, sender->td_end - receiver->td_maxwin - 1) ? + before(ack, receiver->td_end + 1) ? + after(ack, receiver->td_end - MAXACKWINDOW(receiver)) ? "BUG" + : "ACK is under the lower bound (possibly overly delayed ACK)" + : "ACK is over the upper bound (ACKed data has never seen yet)" + : "SEQ is under the lower bound (retransmitted already ACKed data)" + : "SEQ is over the upper bound (over the window of the receiver)"); + res = ip_ct_tcp_be_liberal && !tcph->rst; + } + + DEBUGP("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u receiver end=%u maxend=%u maxwin=%u\n", + res, sender->td_end, sender->td_maxend, sender->td_maxwin, + receiver->td_end, receiver->td_maxend, receiver->td_maxwin); + + return res; } +/* Update sender->td_end after NAT successfully mangled the packet */ +void ip_conntrack_tcp_update(struct ip_conntrack *conntrack, int dir, + struct iphdr *iph, size_t newlen, + struct tcphdr *tcph) +{ + __u32 end; +#ifdef DDEBUGP + struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[dir]; + struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[!dir]; +#endif + + end = SEGMENT_SEQ_PLUS_LEN(ntohl(tcph->seq), newlen, iph, tcph); + + WRITE_LOCK(&tcp_lock); + /* + * We have to worry for the ack in the reply packet only... + */ + if (after(end, conntrack->proto.tcp.seen[dir].td_end)) + conntrack->proto.tcp.seen[dir].td_end = end; + WRITE_UNLOCK(&tcp_lock); + DEBUGP("tcp_update: sender end=%u maxend=%u maxwin=%u scale=%i receiver end=%u maxend=%u maxwin=%u scale=%i\n", + sender->td_end, sender->td_maxend, sender->td_maxwin, sender->td_scale, + receiver->td_end, receiver->td_maxend, receiver->td_maxwin, receiver->td_scale); +} + + /* Returns verdict for packet, or -1 for invalid. */ static int tcp_packet(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len, enum ip_conntrack_info ctinfo) { - enum tcp_conntrack newconntrack, oldtcpstate; + enum tcp_conntrack new_state, old_state; + enum ip_conntrack_dir dir; struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); /* We're guaranteed to have the base header, but maybe not the @@ -159,48 +637,65 @@ } WRITE_LOCK(&tcp_lock); - oldtcpstate = conntrack->proto.tcp.state; - newconntrack + old_state = conntrack->proto.tcp.state; + dir = CTINFO2DIR(ctinfo); + + new_state = tcp_conntracks - [CTINFO2DIR(ctinfo)] - [get_conntrack_index(tcph)][oldtcpstate]; + [dir] + [get_conntrack_index(tcph)][old_state]; + + if (new_state == TCP_CONNTRACK_SYN_SENT + && old_state >= TCP_CONNTRACK_TIME_WAIT) { + /* Attempt to reopen a closed connection. + * Delete this connection and look up again. */ + WRITE_UNLOCK(&tcp_lock); + if (del_timer(&conntrack->timeout)) + conntrack->timeout.function((unsigned long)conntrack); + return NF_REPEAT; + } else if (!(new_state == TCP_CONNTRACK_MAX + || tcp_in_window(&conntrack->proto.tcp.seen[dir], + &conntrack->proto.tcp.seen[!dir], + iph, len, tcph))) + new_state = TCP_CONNTRACK_MAX; + + DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->daddr), ntohs(tcph->dest), + (tcph->syn ? 1 : 0), (tcph->ack ? 1 : 0), (tcph->fin ? 1 : 0), (tcph->rst ? 1 : 0), + old_state, new_state); /* Invalid */ - if (newconntrack == TCP_CONNTRACK_MAX) { + if (new_state == TCP_CONNTRACK_MAX) { DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n", - CTINFO2DIR(ctinfo), get_conntrack_index(tcph), - conntrack->proto.tcp.state); + dir, get_conntrack_index(tcph), + old_state); WRITE_UNLOCK(&tcp_lock); return -1; } - conntrack->proto.tcp.state = newconntrack; - - /* Poor man's window tracking: record SYN/ACK for handshake check */ - if (oldtcpstate == TCP_CONNTRACK_SYN_SENT - && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY - && tcph->syn && tcph->ack) - conntrack->proto.tcp.handshake_ack - = htonl(ntohl(tcph->seq) + 1); + conntrack->proto.tcp.state = new_state; WRITE_UNLOCK(&tcp_lock); - /* If only reply is a RST, we can consider ourselves not to - have an established connection: this is a fairly common - problem case, so we can delete the conntrack - immediately. --RR */ - if (!(conntrack->status & IPS_SEEN_REPLY) && tcph->rst) { - if (del_timer(&conntrack->timeout)) - conntrack->timeout.function((unsigned long)conntrack); - } else { - /* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV */ - if (oldtcpstate == TCP_CONNTRACK_SYN_RECV - && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL - && tcph->ack && !tcph->syn - && tcph->ack_seq == conntrack->proto.tcp.handshake_ack) - set_bit(IPS_ASSURED_BIT, &conntrack->status); - - ip_ct_refresh(conntrack, tcp_timeouts[newconntrack]); + if (!(conntrack->status & IPS_SEEN_REPLY)) { + /* If only reply is a RST, we can consider ourselves not to + have an established connection: this is a fairly common + problem case, so we can delete the conntrack + immediately. --RR */ + if (tcph->rst) { + if (del_timer(&conntrack->timeout)) + conntrack->timeout.function((unsigned long)conntrack); + + return NF_ACCEPT; + } else { + /* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV + or a valid answer for an picked up connection */ + if ((old_state == TCP_CONNTRACK_SYN_RECV + || old_state == TCP_CONNTRACK_ESTABLISHED) + && new_state == TCP_CONNTRACK_ESTABLISHED) + set_bit(IPS_ASSURED_BIT, &conntrack->status); + } } + ip_ct_refresh(conntrack, *tcp_timeouts[new_state]); return NF_ACCEPT; } @@ -209,25 +704,71 @@ static int tcp_new(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len) { - enum tcp_conntrack newconntrack; + enum tcp_conntrack new_state; struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); /* Don't need lock here: this conntrack not in circulation yet */ - newconntrack + new_state = tcp_conntracks[0][get_conntrack_index(tcph)] [TCP_CONNTRACK_NONE]; /* Invalid: delete conntrack */ - if (newconntrack == TCP_CONNTRACK_MAX) { + if (new_state == TCP_CONNTRACK_MAX) { DEBUGP("ip_conntrack_tcp: invalid new deleting.\n"); return 0; } - conntrack->proto.tcp.state = newconntrack; + if (new_state == TCP_CONNTRACK_SYN_SENT) { + conntrack->proto.tcp.seen[0].td_end = + SEGMENT_SEQ_PLUS_LEN(ntohl(tcph->seq), len, iph, tcph); + conntrack->proto.tcp.seen[0].td_maxwin = ntohs(tcph->window); + if (conntrack->proto.tcp.seen[0].td_maxwin == 0) + conntrack->proto.tcp.seen[0].td_maxwin = 1; + conntrack->proto.tcp.seen[0].td_maxend = + conntrack->proto.tcp.seen[0].td_end; + conntrack->proto.tcp.seen[0].td_scale = tcp_scale_option(iph, tcph); + } else { + /* + * We are in the middle of a connection, + * its history is lost for us. + * Let's try to use the data from the packet. + */ + conntrack->proto.tcp.seen[0].td_end = + SEGMENT_SEQ_PLUS_LEN(ntohl(tcph->seq), len, iph, tcph); + conntrack->proto.tcp.seen[0].td_maxwin = ntohs(tcph->window); + if (conntrack->proto.tcp.seen[0].td_maxwin == 0) + conntrack->proto.tcp.seen[0].td_maxwin = 1; + conntrack->proto.tcp.seen[0].td_maxend = + conntrack->proto.tcp.seen[0].td_end + + conntrack->proto.tcp.seen[0].td_maxwin; + conntrack->proto.tcp.seen[0].td_scale = 0; + } + + conntrack->proto.tcp.seen[1].td_end = 0; + conntrack->proto.tcp.seen[1].td_maxend = 0; + conntrack->proto.tcp.seen[1].td_maxwin = 1; + conntrack->proto.tcp.seen[1].td_scale = 0; + + conntrack->proto.tcp.state = new_state; + return 1; } +EXPORT_SYMBOL(ip_conntrack_tcp_update); + +static int tcp_exp_matches_pkt(struct ip_conntrack_expect *exp, + struct sk_buff **pskb) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); + unsigned int datalen; + + datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4; + + return between(exp->seq, ntohl(tcph->seq), ntohl(tcph->seq) + datalen); +} + struct ip_conntrack_protocol ip_conntrack_protocol_tcp = { { NULL, NULL }, IPPROTO_TCP, "tcp", tcp_pkt_to_tuple, tcp_invert_tuple, tcp_print_tuple, tcp_print_conntrack, - tcp_packet, tcp_new, NULL }; + tcp_packet, tcp_new, NULL, tcp_exp_matches_pkt, NULL }; diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_proto_udp.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_proto_udp.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_proto_udp.c 2001-04-27 23:15:01.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_proto_udp.c 2002-07-07 00:19:39.000000000 +0200 @@ -6,8 +6,8 @@ #include #include -#define UDP_TIMEOUT (30*HZ) -#define UDP_STREAM_TIMEOUT (180*HZ) +unsigned long ip_ct_udp_timeout = 30*HZ; +unsigned long ip_ct_udp_timeout_stream = 180*HZ; static int udp_pkt_to_tuple(const void *datah, size_t datalen, struct ip_conntrack_tuple *tuple) @@ -52,11 +52,11 @@ /* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ if (conntrack->status & IPS_SEEN_REPLY) { - ip_ct_refresh(conntrack, UDP_STREAM_TIMEOUT); + ip_ct_refresh(conntrack, ip_ct_udp_timeout_stream); /* Also, more likely to be important, and not a probe */ set_bit(IPS_ASSURED_BIT, &conntrack->status); } else - ip_ct_refresh(conntrack, UDP_TIMEOUT); + ip_ct_refresh(conntrack, ip_ct_udp_timeout); return NF_ACCEPT; } @@ -71,4 +71,4 @@ struct ip_conntrack_protocol ip_conntrack_protocol_udp = { { NULL, NULL }, IPPROTO_UDP, "udp", udp_pkt_to_tuple, udp_invert_tuple, udp_print_tuple, udp_print_conntrack, - udp_packet, udp_new, NULL }; + udp_packet, udp_new, NULL, NULL, NULL }; diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,344 @@ +/* RPC extension for IP connection tracking. */ +/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */ +/* Yes, my english is very poor :-) */ + +/* 25 Mar 2002 Harald Welte + * Ported to netfilter 'newnat' API + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +DECLARE_RWLOCK(ipct_rpc_tcp_lock); +#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock) +#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock) +#include + + /* For future conections RPC, using client's cache bindings */ + +LIST_HEAD(expect_rpc_list_tcp); +LIST_HEAD(request_p_list_tcp); + +void delete_request_p(unsigned long request_p_ul) { + struct request_p *p = (void *)request_p_ul; + + WRITE_LOCK(&ipct_rpc_tcp_lock); + LIST_DELETE(&request_p_list_tcp, p); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(p); + return; + +} + +void delete_binding(unsigned long rpc_binding_ul) { + struct expect_rpc *l = (void *)rpc_binding_ul; + + WRITE_LOCK(&ipct_rpc_tcp_lock); + LIST_DELETE(&expect_rpc_list_tcp, l); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(l); + return; +} + +void exp_cl(struct expect_rpc * t) { + WRITE_LOCK(&ipct_rpc_tcp_lock); + del_timer(&t->timeout); + LIST_DELETE(&expect_rpc_list_tcp, t); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(t); + return; +} + + +/* Clean timers kernel list in unregister to deny crash */ +void clean_expect(struct list_head *list){ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if(list_empty(list)) return; + + while(first != temp) { + aux = temp->next; + exp_cl((struct expect_rpc *)temp); + temp = aux; + } + exp_cl((struct expect_rpc *)temp); + return; +} + +void req_cl(struct request_p * r){ + WRITE_LOCK(&ipct_rpc_tcp_lock); + del_timer(&r->timeout); + LIST_DELETE(&request_p_list_tcp, r); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(r); + return; +} + +/* Clean timers kernel list in unregister to deny crash */ +void clean_request(struct list_head *list){ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if(list_empty(list)) return; + + while(first != temp) { + aux = temp->next; + req_cl((struct request_p *)temp); + temp = aux; + } + req_cl((struct request_p *)temp); + return; +} + + +void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip, + u_int16_t port){ + struct request_p *req_p; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp, + struct request_p *, xid, ip, port); + + if(req_p){ + /* Refresh timeout */ + if (del_timer(&req_p->timeout)) { + req_p->timeout.expires = jiffies + EXP; + add_timer(&req_p->timeout); + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + + /* Allocate new request_p */ + req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC); + if(!req_p) { + DEBUGRPC("Can't allocate request_p\n"); + return; + } + *req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, + { { NULL, NULL }, jiffies + EXP, (unsigned long)req_p, + NULL }}); + + /*Initialize timer */ + init_timer(&req_p->timeout); + req_p->timeout.function = delete_request_p; + add_timer(&req_p->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + list_prepend(&request_p_list_tcp, req_p); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + +} + +/* Update Internal expect list */ +void binding(u_int32_t ip, u_int16_t port, u_int32_t ip_rmt, u_int16_t proto){ + struct expect_rpc *rpc_binding; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + rpc_binding = LIST_FIND(&expect_rpc_list_tcp, expect_rpc_cmp, + struct expect_rpc *, port, ip, ip_rmt, proto); + + if(rpc_binding){ + /* Refresh timeout */ + if (del_timer(&rpc_binding->timeout)) { + rpc_binding->timeout.expires = jiffies + EXPIRES; + add_timer(&rpc_binding->timeout); + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + + /* Allocate new binding */ + rpc_binding = (struct expect_rpc *) kmalloc(sizeof(struct expect_rpc), GFP_ATOMIC); + if(!rpc_binding) { + DEBUGRPC("Can't allocate rpc_binding\n"); + return; + } + *rpc_binding = ((struct expect_rpc) {{ NULL, NULL }, ip, port, ip_rmt, proto, + { { NULL, NULL }, jiffies + EXPIRES, (unsigned long)rpc_binding, + NULL }}); + + /*Initialize timer */ + init_timer(&rpc_binding->timeout); + rpc_binding->timeout.function = delete_binding; + add_timer(&rpc_binding->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + list_prepend(&expect_rpc_list_tcp, rpc_binding); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + +} + +/* RPC using TCP - help */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + const u_int32_t *data = (const u_int32_t *)tcph + tcph->doff; + + /* Translstion's buffer for XDR */ + u_int16_t port_buf; + + size_t tcplen = len - iph->ihl * 4; + + /* Important for tracking */ + int dir = CTINFO2DIR(ctinfo); + u_int32_t xid; + struct request_p *req_p; + + /* This works for packets like handshake packets, ignore */ + if (len == ((tcph->doff + iph->ihl) * 4)) return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED) { + DEBUGRPC("RPC_TCP: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) { + DEBUGRPC("RPC: tcplen = %u\n", (unsigned)tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcplen, 0))) { + DEBUGRPC("RPC_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, IP_PARTS(iph->saddr), + IP_PARTS(iph->daddr)); + return NF_ACCEPT; + } + + + /* If packet is client packet */ + if(!dir){ + + /* Tests if packet len is ok */ + if((tcplen - (tcph->doff * 4)) != 60) + return NF_ACCEPT; + + /* Jump RPC Packet length in XDR */ + data++; + + /* Get XID */ + xid = *data; + + /* Jump until procedure number */ + data = data + 5; + /* Is one get port procedure? */ + if(IXDR_GET_INT32(data) == 3){ + data++; + + /* Jump Credentials and Verfifier */ + data = data + IXDR_GET_INT32(data) + 2; + data = data + IXDR_GET_INT32(data) + 2; + + /* Get and store protocol in RPC client parameters */ + data = data + 2; + alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip, + ct->tuplehash[dir].tuple.src.u.all); + } + return NF_ACCEPT; + } + + /* Tests if packet len is ok */ + if((tcplen - (tcph->doff * 4)) != 32) + return NF_ACCEPT; + + /* Jump RPC Packet length in XDR */ + data++; + + /* XID is important */ + req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp, + struct request_p *, *data, ct->tuplehash[!dir].tuple.src.ip, + ct->tuplehash[!dir].tuple.src.u.all); + + /* Drop packets for accurate tracking */ + if(!req_p) return NF_DROP; + data = data++; + + /* Verifies if packet is really a reply packet */ + if(IXDR_GET_INT32(data) != 1) { + DEBUGRPC("RPC_TCP: Packet with problems"); + return NF_ACCEPT; + } + data++; + + /* Is status accept? */ + if(IXDR_GET_INT32(data)) return NF_ACCEPT; + data++; + + /* Get Verifier lentgth. Jump verifier */ + data = data + IXDR_GET_INT32(data) + 2; + + /* Is accpet status "sucess"? */ + if(IXDR_GET_INT32(data)) return NF_ACCEPT; + data++; + + /* Get server port number */ + port_buf = (u_int16_t) IXDR_GET_INT32(data); + + /* All right. This binding must be in internal expect list */ + /* If port == 0, service is not registred */ + if(port_buf) + binding(ct->tuplehash[dir].tuple.src.ip, port_buf, + ct->tuplehash[!dir].tuple.src.ip, req_p->proto); + req_cl(req_p); + return NF_ACCEPT; +} + +struct ip_conntrack_helper rpc_tcp = { { NULL, NULL }, + "rpc-tcp", + 0, THIS_MODULE, 1, 0, + { { 0, { __constant_htons(111) } }, + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + help }; + +int init_module(void) +{ + printk("ip_conntrack_rpc_tcp.o\n"); + return ip_conntrack_helper_register(&rpc_tcp); +} + +void cleanup_module(void) +{ + clean_expect(&expect_rpc_list_tcp); + clean_request(&request_p_list_tcp); + ip_conntrack_helper_unregister(&rpc_tcp); +} + +struct module *ip_conntrack_rpc_tcp = THIS_MODULE; +EXPORT_SYMBOL(expect_rpc_list_tcp); +EXPORT_SYMBOL(ip_conntrack_rpc_tcp); +EXPORT_SYMBOL(ipct_rpc_tcp_lock); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_rpc_udp.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_rpc_udp.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_rpc_udp.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_rpc_udp.c 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,336 @@ +/* RPC extension for IP connection tracking. */ +/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */ +/* Yes, my english is very poor :-) */ + +/* + * 25 Mar 2002 Harald Welte + * Port to netfilter 'newnat' API + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_RWLOCK(ipct_rpc_udp_lock); +#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock) +#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock) +#include + + /* For future conections RPC, using client's cache bindings */ +LIST_HEAD(expect_rpc_list_udp); +LIST_HEAD(request_p_list_udp); + +void delete_request_p(unsigned long request_p_ul) { + struct request_p *p = (void *)request_p_ul; + WRITE_LOCK(&ipct_rpc_udp_lock); + LIST_DELETE(&request_p_list_udp, p); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(p); + return; + +} + +void delete_binding(unsigned long rpc_binding_ul) { + struct expect_rpc *l = (void *)rpc_binding_ul; + WRITE_LOCK(&ipct_rpc_udp_lock); + LIST_DELETE(&expect_rpc_list_udp, l); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(l); + return; +} + +void exp_cl(struct expect_rpc * t) { + WRITE_LOCK(&ipct_rpc_udp_lock); + del_timer(&t->timeout); + LIST_DELETE(&expect_rpc_list_udp, t); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(t); + return; +} + + +/* Clean timers in unregister to deny crash */ +void clean_expect(struct list_head *list){ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if(list_empty(list)) return; + + while(first != temp) { + aux = temp->next; + exp_cl((struct expect_rpc *)temp); + temp = aux; + } + exp_cl((struct expect_rpc *)temp); + return; +} + +void req_cl(struct request_p * r){ + WRITE_LOCK(&ipct_rpc_udp_lock); + del_timer(&r->timeout); + LIST_DELETE(&request_p_list_udp, r); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(r); + return; +} + +/* Clean timers in unregister to deny crash */ +void clean_request(struct list_head *list){ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if(list_empty(list)) return; + + while(first != temp) { + aux = temp->next; + req_cl((struct request_p *)temp); + temp = aux; + } + req_cl((struct request_p *)temp); + return; +} + + +void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip, + u_int16_t port){ + struct request_p *req_p; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_udp_lock); + req_p = LIST_FIND(&request_p_list_udp, request_p_cmp, + struct request_p *, xid, ip, port); + + if(req_p){ + /* Refresh timeout */ + if (del_timer(&req_p->timeout)) { + req_p->timeout.expires = jiffies + EXP; + add_timer(&req_p->timeout); + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + + /* Allocate new request_p */ + req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC); + if(!req_p) { + DEBUGRPC("Can't allocate request_p\n"); + return; + } + *req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, + { {NULL, NULL}, jiffies + EXP, (unsigned long)req_p, + NULL }}); + + /*Initialize timer */ + init_timer(&req_p->timeout); + req_p->timeout.function = delete_request_p; + add_timer(&req_p->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_udp_lock); + list_prepend(&request_p_list_udp, req_p); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + +} + +/* Update Internal expect list */ +void binding(u_int32_t ip, u_int16_t port, u_int32_t ip_rmt, u_int16_t proto){ + struct expect_rpc *rpc_binding; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_udp_lock); + rpc_binding = LIST_FIND(&expect_rpc_list_udp, expect_rpc_cmp, + struct expect_rpc *, port, ip, ip_rmt, proto); + + if(rpc_binding){ + /* Refresh timeout */ + if (del_timer(&rpc_binding->timeout)) { + rpc_binding->timeout.expires = jiffies + EXPIRES; + add_timer(&rpc_binding->timeout); + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + + /* Allocate new binding */ + rpc_binding = (struct expect_rpc *) kmalloc(sizeof(struct expect_rpc), GFP_ATOMIC); + if(!rpc_binding) { + DEBUGRPC("Can't allocate rpc_binding\n"); + return; + } + *rpc_binding = ((struct expect_rpc) {{ NULL, NULL }, ip, port, ip_rmt, proto, + { {NULL, NULL}, jiffies + EXPIRES, (unsigned long)rpc_binding, + NULL }}); + + /*Initialize timer */ + init_timer(&rpc_binding->timeout); + rpc_binding->timeout.function = delete_binding; + add_timer(&rpc_binding->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_udp_lock); + list_prepend(&expect_rpc_list_udp, rpc_binding); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + +} + + +/* RPC using UDP - help */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct udphdr *udph = (void *)iph + iph->ihl * 4; + const u_int32_t *data = (const u_int32_t *)udph + 2; + + /* Checksum */ + const u_int16_t *chsm = (const u_int16_t *)udph + 3; + + /* Translstion's buffer for XDR */ + u_int16_t port_buf; + + size_t udplen = len - iph->ihl * 4; + + /* Important for tracking */ + int dir = CTINFO2DIR(ctinfo); + u_int32_t xid; + struct request_p *req_p; + + /* Not whole UDP header */ + if (udplen < sizeof(struct udphdr)) { + DEBUGRPC("RPC_UDP: udplen = %u\n", (unsigned)udplen); + return NF_ACCEPT; + } + + /* Checksum is calculated? */ + if(*chsm){ + /* Checksum invalid? Ignore. */ + if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + csum_partial((char *)udph, udplen, 0))) { + DEBUGRPC("RPC_help_UDP: bad csum: %p %u%u.%u.%u.%u %u.%u.%u.%u\n", + udph, udplen, IP_PARTS(iph->saddr), + IP_PARTS(iph->daddr)); + return NF_ACCEPT; + } + } + + /* If packet is client packet */ + if (!dir) { + /* Test if packet len is ok */ + if((udplen - sizeof(struct udphdr)) != 56) + return NF_ACCEPT; + /* Get XID */ + xid = *data; + + /* Jump until procedure number */ + data = data + 5; + /* Is one get port procedure? */ + if(IXDR_GET_INT32(data) == 3){ + data++; + + /* Jump Credentials and Verfifier */ + data = data + IXDR_GET_INT32(data) + 2; + data = data + IXDR_GET_INT32(data) + 2; + + /* Get and store protocol in RPC client parameters */ + data = data + 2; + alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip, + ct->tuplehash[dir].tuple.src.u.all); + } + return NF_ACCEPT; + } + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED) { + DEBUGRPC("RPC_UDP: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Test if packet len is ok */ + if((udplen - sizeof(struct udphdr)) != 28) return NF_ACCEPT; + + /* XID is important */ + req_p = LIST_FIND(&request_p_list_udp, request_p_cmp, + struct request_p *, *data, ct->tuplehash[!dir].tuple.src.ip, + ct->tuplehash[!dir].tuple.src.u.all); + + /* Drop packets for accurate tracking */ + if(!req_p) return NF_DROP; + + data = data++; + + /* Verifies if packet is really a reply packet */ + if(IXDR_GET_INT32(data) != 1) { + DEBUGRPC("RPC_UDP: Packet with problems"); + return NF_ACCEPT; + } + data++; + + /* Is status accpet? */ + if(IXDR_GET_INT32(data)) return NF_ACCEPT; + data++; + + /* Get Verifier lentgth. Jump verifier */ + data = data + IXDR_GET_INT32(data) + 2; + + /* Is accpet status "sucess"? */ + if(IXDR_GET_INT32(data)) return NF_ACCEPT; + data++; + + /* Get server port number */ + port_buf = (u_int16_t) IXDR_GET_INT32(data); + + /* All right. This binding must be in internal expect list */ + /* If port == 0, service is not registred */ + if(port_buf) + binding(ct->tuplehash[dir].tuple.src.ip, port_buf, + ct->tuplehash[!dir].tuple.src.ip, req_p->proto); + req_cl(req_p); + return NF_ACCEPT; +} + +struct ip_conntrack_helper rpc_udp = { { NULL, NULL }, + "rpc-udp", + 0, THIS_MODULE, 1, 0, + { { 0, { __constant_htons(111) } }, + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + help }; + +int init_module(void) +{ + printk("ip_conntrack_rpc_udp.o\n"); + return ip_conntrack_helper_register(&rpc_udp); +} + +void cleanup_module(void) +{ + clean_expect(&expect_rpc_list_udp); + clean_request(&request_p_list_udp); + ip_conntrack_helper_unregister(&rpc_udp); +} + +struct module *ip_conntrack_rpc_udp = THIS_MODULE; +EXPORT_SYMBOL(expect_rpc_list_udp); +EXPORT_SYMBOL(ip_conntrack_rpc_udp); +EXPORT_SYMBOL(ipct_rpc_udp_lock); + diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_standalone.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_standalone.c 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_standalone.c 2002-07-07 00:19:39.000000000 +0200 @@ -7,6 +7,7 @@ /* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General Public Licence. */ +#include #include #include #include @@ -15,6 +16,10 @@ #include #include #include +#ifdef CONFIG_SYSCTL +#include +#endif +#include #include #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock) @@ -35,6 +40,11 @@ struct module *ip_conntrack_module = THIS_MODULE; MODULE_LICENSE("GPL"); +static int kill_proto(const struct ip_conntrack *i, void *data) +{ + return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == + *((u_int8_t *) data)); +} static unsigned int print_tuple(char *buffer, const struct ip_conntrack_tuple *tuple, @@ -56,7 +66,13 @@ { unsigned int len; - len = sprintf(buffer, "EXPECTING: proto=%u ", + if (expect->expectant->helper->timeout) + len = sprintf(buffer, "EXPECTING: %lu ", + timer_pending(&expect->timeout) + ? (expect->timeout.expires - jiffies)/HZ : 0); + else + len = sprintf(buffer, "EXPECTING: - "); + len += sprintf(buffer + len, "proto=%u ", expect->tuple.dst.protonum); len += print_tuple(buffer + len, &expect->tuple, __find_proto(expect->tuple.dst.protonum)); @@ -226,6 +242,114 @@ static struct nf_hook_ops ip_conntrack_local_in_ops = { { NULL, NULL }, ip_confirm, PF_INET, NF_IP_LOCAL_IN, NF_IP_PRI_LAST-1 }; +/* Sysctl support */ + +#ifdef CONFIG_SYSCTL + +/* From ip_conntrack_core.c */ +extern int ip_conntrack_max; + +/* From ip_conntrack_proto_tcp.c */ +extern unsigned long ip_ct_tcp_timeout_none; +extern unsigned long ip_ct_tcp_timeout_syn_sent; +extern unsigned long ip_ct_tcp_timeout_syn_recv; +extern unsigned long ip_ct_tcp_timeout_established; +extern unsigned long ip_ct_tcp_timeout_fin_wait; +extern unsigned long ip_ct_tcp_timeout_close_wait; +extern unsigned long ip_ct_tcp_timeout_last_ack; +extern unsigned long ip_ct_tcp_timeout_time_wait; +extern unsigned long ip_ct_tcp_timeout_close; +extern unsigned long ip_ct_tcp_timeout_listen; +extern int ip_ct_tcp_log_invalid_scale; +extern int ip_ct_tcp_log_out_of_window; +extern int ip_ct_tcp_be_liberal; + +/* From ip_conntrack_proto_udp.c */ +extern unsigned long ip_ct_udp_timeout; +extern unsigned long ip_ct_udp_timeout_stream; + +/* From ip_conntrack_proto_icmp.c */ +extern unsigned long ip_ct_icmp_timeout; + +/* From ip_conntrack_proto_icmp.c */ +extern unsigned long ip_ct_generic_timeout; + +static struct ctl_table_header *ip_ct_sysctl_header; + +static ctl_table ip_ct_sysctl_table[19] = { + {NET_IPV4_NF_CONNTRACK_MAX, "ip_conntrack_max", + &ip_conntrack_max, sizeof(int), 0644, NULL, + &proc_dointvec}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_NONE, "ip_conntrack_tcp_timeout_none", + &ip_ct_tcp_timeout_none, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT, "ip_conntrack_tcp_timeout_syn_sent", + &ip_ct_tcp_timeout_syn_sent, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV, "ip_conntrack_tcp_timeout_syn_recv", + &ip_ct_tcp_timeout_syn_recv, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED, "ip_conntrack_tcp_timeout_established", + &ip_ct_tcp_timeout_established, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT, "ip_conntrack_tcp_timeout_fin_wait", + &ip_ct_tcp_timeout_fin_wait, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT, "ip_conntrack_tcp_timeout_close_wait", + &ip_ct_tcp_timeout_close_wait, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK, "ip_conntrack_tcp_timeout_last_ack", + &ip_ct_tcp_timeout_last_ack, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT, "ip_conntrack_tcp_timeout_time_wait", + &ip_ct_tcp_timeout_time_wait, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE, "ip_conntrack_tcp_timeout_close", + &ip_ct_tcp_timeout_close, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LISTEN, "ip_conntrack_tcp_timeout_listen", + &ip_ct_tcp_timeout_listen, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_LOG_INVALID_SCALE, "ip_conntrack_tcp_log_invalid_scale", + &ip_ct_tcp_log_invalid_scale, sizeof(unsigned int), 0644, NULL, + &proc_dointvec}, + {NET_IPV4_NF_CONNTRACK_TCP_LOG_OUT_OF_WINDOW, "ip_conntrack_tcp_log_out_of_window", + &ip_ct_tcp_log_out_of_window, sizeof(unsigned int), 0644, NULL, + &proc_dointvec}, + {NET_IPV4_NF_CONNTRACK_TCP_BE_LIBERAL, "ip_conntrack_tcp_be_liberal", + &ip_ct_tcp_be_liberal, sizeof(unsigned int), 0644, NULL, + &proc_dointvec}, + {NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT, "ip_conntrack_udp_timeout", + &ip_ct_udp_timeout, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT_STREAM, "ip_conntrack_udp_timeout_stream", + &ip_ct_udp_timeout_stream, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT, "ip_conntrack_icmp_timeout", + &ip_ct_icmp_timeout, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT, "ip_conntrack_generic_timeout", + &ip_ct_generic_timeout, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {0} +}; + +static ctl_table ip_ct_netfilter_table[] = { + {NET_IPV4_NETFILTER, "netfilter", NULL, 0, 0555, ip_ct_sysctl_table, 0, 0, 0, 0, 0}, + {0} +}; + +static ctl_table ip_ct_ipv4_table[] = { + {NET_IPV4, "ipv4", NULL, 0, 0555, ip_ct_netfilter_table, 0, 0, 0, 0, 0}, + {0} +}; + +static ctl_table ip_ct_net_table[] = { + {CTL_NET, "net", NULL, 0, 0555, ip_ct_ipv4_table, 0, 0, 0, 0, 0}, + {0} +}; +#endif + static int init_or_cleanup(int init) { struct proc_dir_entry *proc; @@ -261,10 +385,20 @@ printk("ip_conntrack: can't register local in hook.\n"); goto cleanup_inoutandlocalops; } +#ifdef CONFIG_SYSCTL + ip_ct_sysctl_header = register_sysctl_table(ip_ct_net_table, 0); + if (ip_ct_sysctl_header == NULL) { + printk("ip_conntrack: can't register to sysctl.\n"); + goto cleanup; + } +#endif return ret; cleanup: +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(ip_ct_sysctl_header); +#endif nf_unregister_hook(&ip_conntrack_local_in_ops); cleanup_inoutandlocalops: nf_unregister_hook(&ip_conntrack_out_ops); @@ -304,12 +438,24 @@ return ret; } -/* FIXME: Implement this --RR */ -#if 0 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 */ + LIST_DELETE(&protocol_list, proto); + WRITE_UNLOCK(&ip_conntrack_lock); + + /* Somebody could be still looking at the proto in bh. */ + br_write_lock_bh(BR_NETPROTO_LOCK); + br_write_unlock_bh(BR_NETPROTO_LOCK); + + /* Remove all contrack entries for this protocol */ + ip_ct_selective_cleanup(kill_proto, &proto->proto); + + MOD_DEC_USE_COUNT; } -#endif static int __init init(void) { @@ -325,6 +471,7 @@ module_exit(fini); EXPORT_SYMBOL(ip_conntrack_protocol_register); +EXPORT_SYMBOL(ip_conntrack_protocol_unregister); EXPORT_SYMBOL(invert_tuplepr); EXPORT_SYMBOL(ip_conntrack_alter_reply); EXPORT_SYMBOL(ip_conntrack_destroyed); @@ -334,7 +481,12 @@ EXPORT_SYMBOL(ip_conntrack_helper_unregister); EXPORT_SYMBOL(ip_ct_selective_cleanup); EXPORT_SYMBOL(ip_ct_refresh); +EXPORT_SYMBOL(ip_ct_find_proto); +EXPORT_SYMBOL(ip_ct_find_helper); EXPORT_SYMBOL(ip_conntrack_expect_related); +EXPORT_SYMBOL(ip_conntrack_change_expect); +EXPORT_SYMBOL(ip_conntrack_unexpect_related); EXPORT_SYMBOL(ip_conntrack_tuple_taken); EXPORT_SYMBOL(ip_ct_gather_frags); EXPORT_SYMBOL(ip_conntrack_htable_size); +EXPORT_SYMBOL(ip_conntrack_lock); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_talk.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_talk.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_conntrack_talk.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_conntrack_talk.c 2002-07-07 00:19:39.000000000 +0200 @@ -0,0 +1,362 @@ +/* + * talk extension for IP connection tracking. + * Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + ** + * Module load syntax: + * insmod ip_nat_talk.o talk=[0|1] ntalk=[0|1] ntalk2=[01] + * + * talk=[0|1] disable|enable old talk support + * ntalk=[0|1] disable|enable ntalk support + * ntalk2=[0|1] disable|enable ntalk2 support + * + * The default is talk=1 ntalk=1 ntalk2=1 + * + * The helper does not support simultaneous talk requests. + ** + * + * ASCII art on talk protocols + * + * + * caller server callee server + * | \ / + * | \ / + * | \ / + * | / + * | / \ + * 2 | 1 / \ 3 + * caller client ----------- callee client + * 4 + * + * 1. caller client <-> callee server: LOOK_UP, then ANNOUNCE invitation + * ( 2. caller client <-> caller server: LEAVE_INVITE to server ) + * 3. callee client <-> caller server: LOOK_UP invitation + * 4. callee client <-> caller client: talk data channel + * + * [1]: M. Hunter, talk: a historical protocol for interactive communication + * draft-hunter-talk-00.txt + * [2]: D.B. Chapman, E.D. Zwicky: Building Internet Firewalls (O'Reilly) + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Default all talk protocols are supported */ +static int talk = 1; +static int ntalk = 1; +static int ntalk2 = 1; +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("talk connection tracking module"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_PARM +MODULE_PARM(talk, "i"); +MODULE_PARM_DESC(talk, "support (old) talk protocol"); +MODULE_PARM(ntalk, "i"); +MODULE_PARM_DESC(ntalk, "support ntalk protocol"); +MODULE_PARM(ntalk2, "i"); +MODULE_PARM_DESC(ntalk2, "support ntalk2 protocol"); +#endif + +DECLARE_LOCK(ip_talk_lock); +struct module *ip_conntrack_talk = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static int talk_expect(struct ip_conntrack *ct); +static int ntalk_expect(struct ip_conntrack *ct); + +static int (*talk_expectfn[2])(struct ip_conntrack *ct) = {talk_expect, ntalk_expect}; + +static int talk_help_response(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + int talk_port, + u_char mode, + u_char type, + u_char answer, + struct talk_addr *addr) +{ + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_talk_expect *exp_talk_info = &exp->help.exp_talk_info; + + DEBUGP("ip_ct_talk_help_response: %u.%u.%u.%u:%u, type %d answer %d\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + type, answer); + + if (!(answer == SUCCESS && type == mode)) + return NF_ACCEPT; + + memset(&expect, 0, sizeof(expect)); + + if (type == ANNOUNCE) { + + DEBUGP("ip_ct_talk_help_response: ANNOUNCE\n"); + + /* update the talk info */ + LOCK_BH(&ip_talk_lock); + exp_talk_info->port = htons(talk_port); + + /* expect callee client -> caller server message */ + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[dir].tuple.src.ip, + { 0 } }, + { ct->tuplehash[dir].tuple.dst.ip, + { htons(talk_port) }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = talk_expectfn[talk_port - TALK_PORT]; + + DEBUGP("ip_ct_talk_help_response: callee client %u.%u.%u.%u:%u -> caller daemon %u.%u.%u.%u:%u!\n", + NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.udp.port), + NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.udp.port)); + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, &expect); + UNLOCK_BH(&ip_talk_lock); + } + if (type == LOOK_UP) { + + DEBUGP("ip_ct_talk_help_response: LOOK_UP\n"); + + /* update the talk info */ + LOCK_BH(&ip_talk_lock); + exp_talk_info->port = addr->ta_port; + + /* expect callee client -> caller client connection */ + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { addr->ta_addr, + { addr->ta_port }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; + + DEBUGP("ip_ct_talk_help_response: callee client %u.%u.%u.%u:%u -> caller client %u.%u.%u.%u:%u!\n", + NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), + NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, &expect); + UNLOCK_BH(&ip_talk_lock); + } + + return NF_ACCEPT; +} + +/* FIXME: This should be in userspace. Later. */ +static int talk_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + int talk_port, + u_char mode) +{ + struct udphdr *udph = (void *)iph + iph->ihl * 4; + const char *data = (const char *)udph + sizeof(struct udphdr); + int dir = CTINFO2DIR(ctinfo); + size_t udplen; + + DEBUGP("ip_ct_talk_help: help entered\n"); + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ip_ct_talk_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole UDP header? */ + udplen = len - iph->ihl * 4; + if (udplen < sizeof(struct udphdr)) { + DEBUGP("ip_ct_talk_help: too short for udph, udplen = %u\n", (unsigned)udplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + csum_partial((char *)udph, udplen, 0))) { + DEBUGP("ip_ct_talk_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + udph, udplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + DEBUGP("ip_ct_talk_help: %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(udph->source), NIPQUAD(iph->daddr), ntohs(udph->dest)); + + if (dir == IP_CT_DIR_ORIGINAL) + return NF_ACCEPT; + + if (talk_port == TALK_PORT + && udplen == sizeof(struct udphdr) + sizeof(struct talk_response)) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct talk_response *)data)->type, + ((struct talk_response *)data)->answer, + &(((struct talk_response *)data)->addr)); + else if (talk_port == NTALK_PORT + && ntalk + && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_response) + && ((struct ntalk_response *)data)->vers == NTALK_VERSION) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct ntalk_response *)data)->type, + ((struct ntalk_response *)data)->answer, + &(((struct ntalk_response *)data)->addr)); + else if (talk_port == NTALK_PORT + && ntalk2 + && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_response) + && ((struct ntalk2_response *)data)->vers == NTALK2_VERSION) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct ntalk2_response *)data)->type, + ((struct ntalk2_response *)data)->answer, + &(((struct ntalk2_response *)data)->addr)); + else { + DEBUGP("ip_ct_talk_help: not ntalk/ntalk2 response, datalen %u != %u or %u + max 256\n", + (unsigned)udplen - sizeof(struct udphdr), + sizeof(struct ntalk_response), sizeof(struct ntalk2_response)); + return NF_ACCEPT; + } +} + +static int lookup_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, TALK_PORT, LOOK_UP); +} + +static int lookup_nhelp(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, NTALK_PORT, LOOK_UP); +} + +static struct ip_conntrack_helper lookup_helpers[2] = + { { { NULL, NULL }, + "talk", /* name */ + 0, /* flags */ + NULL, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(TALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + lookup_help }, /* helper */ + { { NULL, NULL }, + "ntalk", /* name */ + 0, /* flags */ + NULL, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(NTALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + lookup_nhelp } /* helper */ + }; + +static int talk_expect(struct ip_conntrack *ct) +{ + DEBUGP("ip_conntrack_talk: calling talk_expectfn for ct %p\n", ct); + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &lookup_helpers[0]; + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +static int ntalk_expect(struct ip_conntrack *ct) +{ + DEBUGP("ip_conntrack_talk: calling ntalk_expectfn for ct %p\n", ct); + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &lookup_helpers[1]; + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, TALK_PORT, ANNOUNCE); +} + +static int nhelp(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, NTALK_PORT, ANNOUNCE); +} + +static struct ip_conntrack_helper talk_helpers[2] = + { { { NULL, NULL }, + "talk", /* name */ + 0, /* flags */ + THIS_MODULE, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(TALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + help }, /* helper */ + { { NULL, NULL }, + "ntalk", /* name */ + 0, /* flags */ + THIS_MODULE, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(NTALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + nhelp } /* helper */ + }; + +static int __init init(void) +{ + if (talk > 0) + ip_conntrack_helper_register(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_conntrack_helper_register(&talk_helpers[1]); + + return 0; +} + +static void __exit fini(void) +{ + if (talk > 0) + ip_conntrack_helper_unregister(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_conntrack_helper_unregister(&talk_helpers[1]); +} + +#ifdef CONFIG_IP_NF_NAT_NEEDED +EXPORT_SYMBOL(ip_talk_lock); +#endif + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_fw_compat_masq.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_fw_compat_masq.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_fw_compat_masq.c 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_fw_compat_masq.c 2002-07-07 00:19:39.000000000 +0200 @@ -130,7 +130,7 @@ struct ip_conntrack *ct; int ret; - protocol = find_proto(iph->protocol); + protocol = ip_ct_find_proto(iph->protocol); /* We don't feed packets to conntrack system unless we know they're part of an connection already established by an diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_fw_compat_redir.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_fw_compat_redir.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_fw_compat_redir.c 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_fw_compat_redir.c 2002-07-07 00:19:39.000000000 +0200 @@ -43,7 +43,7 @@ netplay... */ \ printk("ASSERT: %s:%i(%s)\n", \ __FILE__, __LINE__, __FUNCTION__); \ -} while(0); +} while(0) #else #define IP_NF_ASSERT(x) #endif diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_core.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_core.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_core.c 2001-12-21 19:42:05.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_core.c 2002-07-07 00:19:39.000000000 +0200 @@ -21,10 +21,14 @@ #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock) #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock) +#include +#include +#include #include #include #include #include +#include #include #if 0 @@ -34,6 +38,7 @@ #endif DECLARE_RWLOCK(ip_nat_lock); +DECLARE_RWLOCK_EXTERN(ip_conntrack_lock); /* Calculated at init based on memory size */ static unsigned int ip_nat_htable_size; @@ -314,6 +319,7 @@ * do_extra_mangle last time. */ *other_ipp = saved_ip; +#ifdef CONFIG_IP_NF_NAT_LOCAL if (hooknum == NF_IP_LOCAL_OUT && *var_ipp != orig_dstip && !do_extra_mangle(*var_ipp, other_ipp)) { @@ -324,6 +330,7 @@ * anyway. */ continue; } +#endif /* Count how many others map onto this. */ score = count_maps(tuple->src.ip, tuple->dst.ip, @@ -367,11 +374,13 @@ else { /* Only do extra mangle when required (breaks socket binding) */ +#ifdef CONFIG_IP_NF_NAT_LOCAL if (tuple->dst.ip != mr->range[0].min_ip && hooknum == NF_IP_LOCAL_OUT && !do_extra_mangle(mr->range[0].min_ip, &tuple->src.ip)) return NULL; +#endif tuple->dst.ip = mr->range[0].min_ip; } } @@ -494,7 +503,10 @@ static unsigned int opposite_hook[NF_IP_NUMHOOKS] = { [NF_IP_PRE_ROUTING] = NF_IP_POST_ROUTING, [NF_IP_POST_ROUTING] = NF_IP_PRE_ROUTING, - [NF_IP_LOCAL_OUT] = NF_IP_POST_ROUTING +#ifdef CONFIG_IP_NF_NAT_LOCAL + [NF_IP_LOCAL_OUT] = NF_IP_LOCAL_IN, + [NF_IP_LOCAL_IN] = NF_IP_LOCAL_OUT, +#endif }; unsigned int @@ -621,8 +633,9 @@ } /* If there's a helper, assign it; based on new tuple. */ - info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, - &reply); + if (!conntrack->master) + info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, + &reply); /* It's done. */ info->initialized |= (1 << HOOK2MANIP(hooknum)); @@ -717,6 +730,21 @@ #endif } +static inline int exp_for_packet(struct ip_conntrack_expect *exp, + struct sk_buff **pskb) +{ + struct ip_conntrack_protocol *proto; + int ret = 1; + + READ_LOCK(&ip_conntrack_lock); + proto = ip_ct_find_proto((*pskb)->nh.iph->protocol); + if (proto->exp_matches_pkt) + ret = proto->exp_matches_pkt(exp, pskb); + READ_UNLOCK(&ip_conntrack_lock); + + return ret; +} + /* Do packet manipulations according to binding. */ unsigned int do_bindings(struct ip_conntrack *ct, @@ -728,6 +756,7 @@ unsigned int i; struct ip_nat_helper *helper; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + int is_tcp = (*pskb)->nh.iph->protocol == IPPROTO_TCP; /* Need nat lock to protect against modification, but neither conntrack (referenced) and helper (deleted with @@ -766,11 +795,66 @@ READ_UNLOCK(&ip_nat_lock); if (helper) { + struct ip_conntrack_expect *exp = NULL; + struct list_head *cur_item; + int ret = NF_ACCEPT; + + DEBUGP("do_bindings: helper existing for (%p)\n", ct); + /* Always defragged for helpers */ IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET))); - return helper->help(ct, info, ctinfo, hooknum, pskb); - } else return NF_ACCEPT; + + /* Have to grab read lock before sibling_list traversal */ + READ_LOCK(&ip_conntrack_lock); + list_for_each(cur_item, &ct->sibling_list) { + exp = list_entry(cur_item, struct ip_conntrack_expect, + expected_list); + + /* if this expectation is already established, skip */ + if (exp->sibling) + continue; + + if (exp_for_packet(exp, pskb)) { + /* FIXME: May be true multiple times in the case of UDP!! */ + DEBUGP("calling nat helper (exp=%p) for packet\n", + exp); + ret = helper->help(ct, exp, info, ctinfo, + hooknum, pskb); + if (ret != NF_ACCEPT) { + READ_UNLOCK(&ip_conntrack_lock); + return ret; + } + } + } + /* Helper might want to manip the packet even when there is no expectation */ + if (!exp && 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); + if (ret != NF_ACCEPT) { + READ_UNLOCK(&ip_conntrack_lock); + return ret; + } + } + READ_UNLOCK(&ip_conntrack_lock); + + /* Adjust sequence number only once per packet + * (helper is called at all hooks) */ + if (is_tcp && (hooknum == NF_IP_POST_ROUTING + || hooknum == NF_IP_LOCAL_IN)) { + DEBUGP("ip_nat_core: adjusting sequence number\n"); + /* future: put this in a l4-proto specific function, + * and call this function here. */ + ip_nat_seq_adjust(*pskb, ct, ctinfo); + } + + return ret; + + } else + return NF_ACCEPT; + + /* not reached */ } unsigned int diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_ftp.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_ftp.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_ftp.c 2001-10-31 01:08:12.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_ftp.c 2002-07-07 00:19:39.000000000 +0200 @@ -28,38 +28,30 @@ /* FIXME: Time out? --RR */ -static int +static unsigned int ftp_nat_expected(struct sk_buff **pskb, unsigned int hooknum, struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, - unsigned int *verdict) + struct ip_nat_info *info) { struct ip_nat_multi_range mr; u_int32_t newdstip, newsrcip, newip; - struct ip_ct_ftp *ftpinfo; + struct ip_ct_ftp_expect *exp_ftp_info; + struct ip_conntrack *master = master_ct(ct); + IP_NF_ASSERT(info); IP_NF_ASSERT(master); - IP_NF_ASSERT(masterinfo); IP_NF_ASSERT(!(info->initialized & (1<help.ct_ftp_info; + exp_ftp_info = &ct->master->help.exp_ftp_info; LOCK_BH(&ip_ftp_lock); - if (ftpinfo->is_ftp != 21) { - UNLOCK_BH(&ip_ftp_lock); - DEBUGP("nat_expected: master not ftp\n"); - return 0; - } - if (ftpinfo->ftptype == IP_CT_FTP_PORT - || ftpinfo->ftptype == IP_CT_FTP_EPRT) { + if (exp_ftp_info->ftptype == IP_CT_FTP_PORT + || exp_ftp_info->ftptype == IP_CT_FTP_EPRT) { /* PORT command: make connection go to the client. */ newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; @@ -92,11 +84,9 @@ mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; mr.range[0].min = mr.range[0].max = ((union ip_conntrack_manip_proto) - { htons(ftpinfo->port) }); + { htons(exp_ftp_info->port) }); } - *verdict = ip_nat_setup_info(ct, &mr, hooknum); - - return 1; + return ip_nat_setup_info(ct, &mr, hooknum); } static int @@ -176,27 +166,22 @@ [IP_CT_FTP_EPSV] mangle_epsv_packet }; -static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info, +static int ftp_data_fixup(const struct ip_ct_ftp_expect *ct_ftp_info, struct ip_conntrack *ct, - unsigned int datalen, struct sk_buff **pskb, - enum ip_conntrack_info ctinfo) + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) { u_int32_t newip; struct iphdr *iph = (*pskb)->nh.iph; struct tcphdr *tcph = (void *)iph + iph->ihl*4; u_int16_t port; - struct ip_conntrack_tuple tuple; - /* Don't care about source port */ - const struct ip_conntrack_tuple mask - = { { 0xFFFFFFFF, { 0 } }, - { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF } }; + struct ip_conntrack_tuple newtuple; - memset(&tuple, 0, sizeof(tuple)); MUST_BE_LOCKED(&ip_ftp_lock); - DEBUGP("FTP_NAT: seq %u + %u in %u + %u\n", - ct_ftp_info->seq, ct_ftp_info->len, - ntohl(tcph->seq), datalen); + DEBUGP("FTP_NAT: seq %u + %u in %u\n", + expect->seq, ct_ftp_info->len, + ntohl(tcph->seq)); /* Change address inside packet to match way we're mapping this connection. */ @@ -206,29 +191,34 @@ is */ newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; /* Expect something from client->server */ - tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; - tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + newtuple.src.ip = + ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newtuple.dst.ip = + ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; } else { /* PORT command: must be where server thinks client is */ newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; /* Expect something from server->client */ - tuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; - tuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + newtuple.src.ip = + ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newtuple.dst.ip = + ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; } - tuple.dst.protonum = IPPROTO_TCP; + newtuple.dst.protonum = IPPROTO_TCP; + newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port; /* Try to get same port: if not, try to change it. */ for (port = ct_ftp_info->port; port != 0; port++) { - tuple.dst.u.tcp.port = htons(port); + newtuple.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(ct, &tuple, &mask, NULL) == 0) + if (ip_conntrack_change_expect(expect, &newtuple) == 0) break; } if (port == 0) return 0; if (!mangle[ct_ftp_info->ftptype](pskb, newip, port, - ct_ftp_info->seq - ntohl(tcph->seq), + expect->seq - ntohl(tcph->seq), ct_ftp_info->len, ct, ctinfo)) return 0; @@ -236,6 +226,7 @@ } static unsigned int help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, struct ip_nat_info *info, enum ip_conntrack_info ctinfo, unsigned int hooknum, @@ -245,13 +236,12 @@ struct tcphdr *tcph = (void *)iph + iph->ihl*4; unsigned int datalen; int dir; - int score; - struct ip_ct_ftp *ct_ftp_info - = &ct->help.ct_ftp_info; - - /* Delete SACK_OK on initial TCP SYNs. */ - if (tcph->syn && !tcph->ack) - ip_nat_delete_sack(*pskb, tcph); + struct ip_ct_ftp_expect *ct_ftp_info; + + if (!exp) + DEBUGP("ip_nat_ftp: no exp!!"); + + ct_ftp_info = &exp->help.exp_ftp_info; /* Only mangle things once: original direction in POST_ROUTING and reply direction on PRE_ROUTING. */ @@ -267,103 +257,87 @@ } datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; - score = 0; LOCK_BH(&ip_ftp_lock); - if (ct_ftp_info->len) { - /* If it's in the right range... */ - score += between(ct_ftp_info->seq, ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - score += between(ct_ftp_info->seq + ct_ftp_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - if (score == 1) { - /* Half a match? This means a partial retransmisison. - It's a cracker being funky. */ - if (net_ratelimit()) { - printk("FTP_NAT: partial packet %u/%u in %u/%u\n", - ct_ftp_info->seq, ct_ftp_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - } + /* If it's in the right range... */ + if (between(exp->seq + ct_ftp_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + if (!ftp_data_fixup(ct_ftp_info, ct, pskb, ctinfo, exp)) { UNLOCK_BH(&ip_ftp_lock); return NF_DROP; - } else if (score == 2) { - if (!ftp_data_fixup(ct_ftp_info, ct, datalen, - pskb, ctinfo)) { - UNLOCK_BH(&ip_ftp_lock); - return NF_DROP; - } - /* skb may have been reallocated */ - iph = (*pskb)->nh.iph; - tcph = (void *)iph + iph->ihl*4; } + } else { + /* Half a match? This means a partial retransmisison. + It's a cracker being funky. */ + if (net_ratelimit()) { + printk("FTP_NAT: partial packet %u/%u in %u/%u\n", + exp->seq, ct_ftp_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + UNLOCK_BH(&ip_ftp_lock); + return NF_DROP; } - UNLOCK_BH(&ip_ftp_lock); - ip_nat_seq_adjust(*pskb, ct, ctinfo); - return NF_ACCEPT; } static struct ip_nat_helper ftp[MAX_PORTS]; -static char ftp_names[MAX_PORTS][6]; - -static struct ip_nat_expect ftp_expect -= { { NULL, NULL }, ftp_nat_expected }; +static char ftp_names[MAX_PORTS][10]; /* Not __exit: called from init() */ static void fini(void) { int i; - for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + for (i = 0; i < ports_c; i++) { DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]); ip_nat_helper_unregister(&ftp[i]); } - - ip_nat_expect_unregister(&ftp_expect); } static int __init init(void) { - int i, ret; + int i, ret = 0; char *tmpname; - ret = ip_nat_expect_register(&ftp_expect); - if (ret == 0) { - if (ports[0] == 0) - ports[0] = 21; - - for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { - - memset(&ftp[i], 0, sizeof(struct ip_nat_helper)); - - ftp[i].tuple.dst.protonum = IPPROTO_TCP; - ftp[i].tuple.src.u.tcp.port = htons(ports[i]); - ftp[i].mask.dst.protonum = 0xFFFF; - ftp[i].mask.src.u.tcp.port = 0xFFFF; - ftp[i].help = help; - - tmpname = &ftp_names[i][0]; - sprintf(tmpname, "ftp%2.2d", i); - ftp[i].name = tmpname; - - DEBUGP("ip_nat_ftp: Trying to register for port %d\n", - ports[i]); - ret = ip_nat_helper_register(&ftp[i]); - - if (ret) { - printk("ip_nat_ftp: error registering helper for port %d\n", ports[i]); - fini(); - return ret; - } - ports_c++; - } + if (ports[0] == 0) + ports[0] = FTP_PORT; - } else { - ip_nat_expect_unregister(&ftp_expect); + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + + memset(&ftp[i], 0, sizeof(struct ip_nat_helper)); + + ftp[i].tuple.dst.protonum = IPPROTO_TCP; + ftp[i].tuple.src.u.tcp.port = htons(ports[i]); + ftp[i].mask.dst.protonum = 0xFFFF; + ftp[i].mask.src.u.tcp.port = 0xFFFF; + ftp[i].help = help; + ftp[i].me = THIS_MODULE; + ftp[i].flags = 0; + ftp[i].expect = ftp_nat_expected; + + tmpname = &ftp_names[i][0]; + if (ports[i] == FTP_PORT) + sprintf(tmpname, "ftp"); + else + sprintf(tmpname, "ftp-%d", i); + ftp[i].name = tmpname; + + DEBUGP("ip_nat_ftp: Trying to register for port %d\n", + ports[i]); + ret = ip_nat_helper_register(&ftp[i]); + + if (ret) { + printk("ip_nat_ftp: error registering " + "helper for port %d\n", ports[i]); + fini(); + return ret; + } + ports_c++; } + return ret; } diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_h323.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_h323.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_h323.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_h323.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,419 @@ +/* + * H.323 'brute force' extension for NAT alteration. + * Jozsef Kadlecsik + * + * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project. + * (http://www.coritel.it/projects/sofia/nat.html) + * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind' + * the unregistered helpers to the conntrack entries. + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("H.323 'brute force' connection tracking module"); +MODULE_LICENSE("GPL"); + +DECLARE_LOCK_EXTERN(ip_h323_lock); +struct module *ip_nat_h323 = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/* FIXME: Time out? --RR */ + +static unsigned int +h225_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info); + +static unsigned int h225_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb); + +static struct ip_nat_helper h245 = + { { NULL, NULL }, + "H.245", /* name */ + 0, /* flags */ + NULL, /* module */ + { { 0, { 0 } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h225_nat_help, /* helper */ + h225_nat_expected /* expectfn */ + }; + +static unsigned int +h225_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + struct ip_nat_multi_range mr; + u_int32_t newdstip, newsrcip, newip; + u_int16_t port; + struct ip_ct_h225_expect *exp_info; + struct ip_ct_h225_master *master_info; + struct ip_conntrack *master = master_ct(ct); + unsigned int is_h225, ret; + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + + IP_NF_ASSERT(!(info->initialized & (1<master->expectant->help.ct_h225_info; + exp_info = &ct->master->help.exp_h225_info; + + LOCK_BH(&ip_h323_lock); + + DEBUGP("master: "); + DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_REPLY].tuple); + DEBUGP("conntrack: "); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + if (exp_info->dir == IP_CT_DIR_ORIGINAL) { + /* Make connection go to the client. */ + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to client)\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } else { + /* Make the connection go to the server */ + newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to server)\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } + port = exp_info->port; + is_h225 = master_info->is_h225 == H225_PORT; + UNLOCK_BH(&ip_h323_lock); + + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) + newip = newsrcip; + else + newip = newdstip; + + DEBUGP("h225_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip)); + + mr.rangesize = 1; + /* We don't want to manip the per-protocol, just the IPs... */ + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + /* ... unless we're doing a MANIP_DST, in which case, make + sure we map to the correct port */ + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { + mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + mr.range[0].min = mr.range[0].max + = ((union ip_conntrack_manip_proto) + { port }); + } + + ret = ip_nat_setup_info(ct, &mr, hooknum); + + if (is_h225) { + DEBUGP("h225_nat_expected: H.225, setting NAT helper for %p\n", ct); + /* NAT expectfn called with ip_nat_lock write-locked */ + info->helper = &h245; + } + return ret; +} + +static int h323_signal_address_fixup(struct ip_conntrack *ct, + struct sk_buff **pskb, + enum ip_conntrack_info ctinfo) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl*4; + char *data = (char *) tcph + tcph->doff * 4; + u_int32_t tcplen = (*pskb)->len - iph->ihl*4; + u_int32_t datalen = tcplen - tcph->doff*4; + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + u_int32_t newip; + u_int16_t port; + int i; + + MUST_BE_LOCKED(&ip_h323_lock); + + DEBUGP("h323_signal_address_fixup: %s %s\n", + between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + ? "yes" : "no", + between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + ? "yes" : "no"); + if (!(between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + || between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen))) + return 1; + + DEBUGP("h323_signal_address_fixup: offsets %u + 6 and %u + 6 in %u\n", + info->offset[IP_CT_DIR_ORIGINAL], + info->offset[IP_CT_DIR_REPLY], + tcplen); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + for (i = 0; i < IP_CT_DIR_MAX; i++) { + DEBUGP("h323_signal_address_fixup: %s %s\n", + info->dir == IP_CT_DIR_ORIGINAL ? "original" : "reply", + i == IP_CT_DIR_ORIGINAL ? "caller" : "callee"); + if (!between(info->seq[i], ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) + continue; + if (!between(info->seq[i] + 6, ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + /* Partial retransmisison. It's a cracker being funky. */ + if (net_ratelimit()) { + printk("H.323_NAT: partial packet %u/6 in %u/%u\n", + info->seq[i], + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + return 0; + } + + /* Change address inside packet to match way we're mapping + this connection. */ + if (i == IP_CT_DIR_ORIGINAL) { + newip = ct->tuplehash[!info->dir].tuple.dst.ip; + port = ct->tuplehash[!info->dir].tuple.dst.u.tcp.port; + } else { + newip = ct->tuplehash[!info->dir].tuple.src.ip; + port = ct->tuplehash[!info->dir].tuple.src.u.tcp.port; + } + + DEBUGP("h323_signal_address_fixup: orig %s IP:port %u.%u.%u.%u:%u\n", + i == IP_CT_DIR_ORIGINAL ? "source" : "dest ", + NIPQUAD(*((u_int32_t *)(data + info->offset[i]))), + ntohs(*((u_int16_t *)(data + info->offset[i] + 4)))); + + /* Modify the packet */ + *(u_int32_t *)(data + info->offset[i]) = newip; + *(u_int16_t *)(data + info->offset[i] + 4) = port; + + DEBUGP("h323_signal_address_fixup: new %s IP:port %u.%u.%u.%u:%u\n", + i == IP_CT_DIR_ORIGINAL ? "source" : "dest ", + NIPQUAD(*((u_int32_t *)(data + info->offset[i]))), + ntohs(*((u_int16_t *)(data + info->offset[i] + 4)))); + } + + /* fix checksum information */ + + (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4, + datalen, 0); + + tcph->check = 0; + tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcph->doff*4, + (*pskb)->csum)); + ip_send_check(iph); + + return 1; +} + +static int h323_data_fixup(struct ip_ct_h225_expect *info, + struct ip_conntrack *ct, + struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) +{ + u_int32_t newip; + u_int16_t port; + struct ip_conntrack_tuple newtuple; + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl*4; + char *data = (char *) tcph + tcph->doff * 4; + u_int32_t tcplen = (*pskb)->len - iph->ihl*4; + struct ip_ct_h225_master *master_info = &ct->help.ct_h225_info; + int is_h225; + + MUST_BE_LOCKED(&ip_h323_lock); + DEBUGP("h323_data_fixup: offset %u + 6 in %u\n", info->offset, tcplen); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + if (!between(expect->seq + 6, ntohl(tcph->seq), + ntohl(tcph->seq) + tcplen - tcph->doff * 4)) { + /* Partial retransmisison. It's a cracker being funky. */ + if (net_ratelimit()) { + printk("H.323_NAT: partial packet %u/6 in %u/%u\n", + expect->seq, + ntohl(tcph->seq), + ntohl(tcph->seq) + tcplen - tcph->doff * 4); + } + return 0; + } + + /* Change address inside packet to match way we're mapping + this connection. */ + if (info->dir == IP_CT_DIR_REPLY) { + /* Must be where client thinks server is */ + newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + /* Expect something from client->server */ + newtuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + } else { + /* Must be where server thinks client is */ + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + /* Expect something from server->client */ + newtuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + } + + is_h225 = (master_info->is_h225 == H225_PORT); + + if (is_h225) { + newtuple.dst.protonum = IPPROTO_TCP; + newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port; + } else { + newtuple.dst.protonum = IPPROTO_UDP; + newtuple.src.u.udp.port = expect->tuple.src.u.udp.port; + } + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(info->port); port != 0; port++) { + if (is_h225) + newtuple.dst.u.tcp.port = htons(port); + else + newtuple.dst.u.udp.port = htons(port); + + if (ip_conntrack_change_expect(expect, &newtuple) == 0) + break; + } + if (port == 0) { + DEBUGP("h323_data_fixup: no free port found!\n"); + return 0; + } + + port = htons(port); + + DEBUGP("h323_data_fixup: orig IP:port %u.%u.%u.%u:%u\n", + NIPQUAD(*((u_int32_t *)(data + info->offset))), + ntohs(*((u_int16_t *)(data + info->offset + 4)))); + + /* Modify the packet */ + *(u_int32_t *)(data + info->offset) = newip; + *(u_int16_t *)(data + info->offset + 4) = port; + + DEBUGP("h323_data_fixup: new IP:port %u.%u.%u.%u:%u\n", + NIPQUAD(*((u_int32_t *)(data + info->offset))), + ntohs(*((u_int16_t *)(data + info->offset + 4)))); + + /* fix checksum information */ + /* FIXME: usually repeated multiple times in the case of H.245! */ + + (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4, + tcplen - tcph->doff*4, 0); + + tcph->check = 0; + tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcph->doff*4, + (*pskb)->csum)); + ip_send_check(iph); + + return 1; +} + +static unsigned int h225_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + int dir; + struct ip_ct_h225_expect *exp_info; + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + dir = CTINFO2DIR(ctinfo); + DEBUGP("nat_h323: dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("nat_h323: Not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + return NF_ACCEPT; + } + + if (!exp) { + LOCK_BH(&ip_h323_lock); + if (!h323_signal_address_fixup(ct, pskb, ctinfo)) { + UNLOCK_BH(&ip_h323_lock); + return NF_DROP; + } + UNLOCK_BH(&ip_h323_lock); + return NF_ACCEPT; + } + + exp_info = &exp->help.exp_h225_info; + + LOCK_BH(&ip_h323_lock); + if (!h323_data_fixup(exp_info, ct, pskb, ctinfo, exp)) { + UNLOCK_BH(&ip_h323_lock); + return NF_DROP; + } + UNLOCK_BH(&ip_h323_lock); + + return NF_ACCEPT; +} + +static struct ip_nat_helper h225 = + { { NULL, NULL }, + "H.225", /* name */ + IP_NAT_HELPER_F_ALWAYS, /* flags */ + THIS_MODULE, /* module */ + { { 0, { __constant_htons(H225_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h225_nat_help, /* helper */ + h225_nat_expected /* expectfn */ + }; + +static int __init init(void) +{ + int ret; + + ret = ip_nat_helper_register(&h225); + + if (ret != 0) + printk("ip_nat_h323: cannot initialize the module!\n"); + + return ret; +} + +static void __exit fini(void) +{ + ip_nat_helper_unregister(&h225); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_helper.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_helper.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_helper.c 2001-12-21 19:42:05.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_helper.c 2002-07-07 00:19:40.000000000 +0200 @@ -1,11 +1,18 @@ /* ip_nat_mangle.c - generic support functions for NAT helpers * - * (C) 2000 by Harald Welte + * (C) 2000-2002 by Harald Welte * * distributed under the terms of GNU GPL + * + * 14 Jan 2002 Harald Welte : + * - add support for SACK adjustment + * 14 Mar 2002 Harald Welte : + * - merge SACK support into newnat API */ #include +#include #include +#include #include #include #include @@ -19,6 +26,8 @@ #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock) #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock) +#include +#include #include #include #include @@ -32,7 +41,7 @@ #define DEBUGP(format, args...) #define DUMP_OFFSET(x) #endif - + DECLARE_LOCK(ip_nat_seqofs_lock); static inline int @@ -199,6 +208,103 @@ return 1; } +/* Adjust one found SACK option including checksum correction */ +static void +sack_adjust(struct tcphdr *tcph, + unsigned char *ptr, + struct ip_nat_seq *natseq) +{ + struct tcp_sack_block *sp = (struct tcp_sack_block *)(ptr+2); + int num_sacks = (ptr[1] - TCPOLEN_SACK_BASE)>>3; + int i; + + for (i = 0; i < num_sacks; i++, sp++) { + u_int32_t new_start_seq, new_end_seq; + + if (after(ntohl(sp->start_seq) - natseq->offset_before, + natseq->correction_pos)) + new_start_seq = ntohl(sp->start_seq) + - natseq->offset_after; + else + new_start_seq = ntohl(sp->start_seq) + - natseq->offset_before; + new_start_seq = htonl(new_start_seq); + + if (after(ntohl(sp->end_seq) - natseq->offset_before, + natseq->correction_pos)) + new_end_seq = ntohl(sp->end_seq) + - natseq->offset_after; + else + new_end_seq = ntohl(sp->end_seq) + - natseq->offset_before; + new_end_seq = htonl(new_end_seq); + + DEBUGP("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", + ntohl(sp->start_seq), new_start_seq, + ntohl(sp->end_seq), new_end_seq); + + tcph->check = + ip_nat_cheat_check(~sp->start_seq, new_start_seq, + ip_nat_cheat_check(~sp->end_seq, + new_end_seq, + tcph->check)); + + sp->start_seq = new_start_seq; + sp->end_seq = new_end_seq; + } +} + + +/* TCP SACK sequence number adjustment, return 0 if sack found and adjusted */ +static inline int +ip_nat_sack_adjust(struct sk_buff *skb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct iphdr *iph; + struct tcphdr *tcph; + unsigned char *ptr; + int length, dir, sack_adjusted = 0; + + iph = skb->nh.iph; + tcph = (void *)iph + iph->ihl*4; + length = (tcph->doff*4)-sizeof(struct tcphdr); + ptr = (unsigned char *)(tcph+1); + + dir = CTINFO2DIR(ctinfo); + + while (length > 0) { + int opcode = *ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return !sack_adjusted; + case TCPOPT_NOP: + length--; + continue; + default: + opsize = *ptr++; + if (opsize > length) /* no partial opts */ + return !sack_adjusted; + 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, + &ct->nat.info.seq[!dir]); + + sack_adjusted = 1; + } + ptr += opsize-2; + length -= opsize; + } + } + return !sack_adjusted; +} + /* TCP sequence number adjustment */ int ip_nat_seq_adjust(struct sk_buff *skb, @@ -243,51 +349,11 @@ tcph->seq = newseq; tcph->ack_seq = newack; - return 0; -} + ip_nat_sack_adjust(skb, ct, ctinfo); -/* Grrr... SACK. Fuck me even harder. Don't want to fix it on the - fly, so blow it away. */ -void -ip_nat_delete_sack(struct sk_buff *skb, struct tcphdr *tcph) -{ - unsigned int i; - u_int8_t *opt = (u_int8_t *)tcph; - - DEBUGP("Seeking SACKPERM in SYN packet (doff = %u).\n", - tcph->doff * 4); - for (i = sizeof(struct tcphdr); i < tcph->doff * 4;) { - DEBUGP("%u ", opt[i]); - switch (opt[i]) { - case TCPOPT_NOP: - case TCPOPT_EOL: - i++; - break; + ip_conntrack_tcp_update(ct, dir, iph, skb->len, tcph); - case TCPOPT_SACK_PERM: - goto found_opt; - - default: - /* Worst that can happen: it will take us over. */ - i += opt[i+1] ?: 1; - } - } - DEBUGP("\n"); - return; - - found_opt: - DEBUGP("\n"); - DEBUGP("Found SACKPERM at offset %u.\n", i); - - /* Must be within TCP header, and valid SACK perm. */ - if (i + opt[i+1] <= tcph->doff*4 && opt[i+1] == 2) { - /* Replace with NOPs. */ - tcph->check - = ip_nat_cheat_check(*((u_int16_t *)(opt + i))^0xFFFF, - (TCPOPT_NOP<<8)|TCPOPT_NOP, tcph->check); - opt[i] = opt[i+1] = TCPOPT_NOP; - } - else DEBUGP("Something wrong with SACK_PERM.\n"); + return 0; } static inline int @@ -297,10 +363,51 @@ return ip_ct_tuple_mask_cmp(tuple, &helper->tuple, &helper->mask); } +#define MODULE_MAX_NAMELEN 32 + int ip_nat_helper_register(struct ip_nat_helper *me) { int ret = 0; + if (me->me && !(me->flags & IP_NAT_HELPER_F_STANDALONE)) { + struct ip_conntrack_helper *ct_helper; + + if ((ct_helper = ip_ct_find_helper(&me->tuple)) + && ct_helper->me) { + __MOD_INC_USE_COUNT(ct_helper->me); + } else { + + /* We are a NAT helper for protocol X. If we need + * respective conntrack helper for protoccol X, compute + * conntrack helper name and try to load module */ + char name[MODULE_MAX_NAMELEN]; + const char *tmp = me->me->name; + + if (strlen(tmp) + 6 > MODULE_MAX_NAMELEN) { + printk(__FUNCTION__ ": unable to " + "compute conntrack helper name " + "from %s\n", tmp); + return -EBUSY; + } + tmp += 6; + sprintf(name, "ip_conntrack%s", tmp); +#ifdef CONFIG_KMOD + if (!request_module(name) + && (ct_helper = ip_ct_find_helper(&me->tuple)) + && ct_helper->me) { + __MOD_INC_USE_COUNT(ct_helper->me); + } else { + printk("unable to load module %s\n", name); + return -EBUSY; + } +#else + printk("unable to load module %s automatically " + "because kernel was compiled without kernel " + "module loader support\n", name); + return -EBUSY; +#endif + } + } WRITE_LOCK(&ip_nat_lock); if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple)) ret = -EBUSY; @@ -327,8 +434,14 @@ void ip_nat_helper_unregister(struct ip_nat_helper *me) { + int found = 0; + WRITE_LOCK(&ip_nat_lock); - LIST_DELETE(&helpers, me); + /* Autoloading conntrack helper might have failed */ + if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple)) { + LIST_DELETE(&helpers, me); + found = 1; + } WRITE_UNLOCK(&ip_nat_lock); /* Someone could be still looking at the helper in a bh. */ @@ -344,5 +457,19 @@ worse. --RR */ ip_ct_selective_cleanup(kill_helper, me); - MOD_DEC_USE_COUNT; + if (found) + MOD_DEC_USE_COUNT; + + /* If we are no standalone NAT helper, we need to decrement usage count + * on our conntrack helper */ + if (me->me && !(me->flags & IP_NAT_HELPER_F_STANDALONE)) { + struct ip_conntrack_helper *ct_helper; + + if ((ct_helper = ip_ct_find_helper(&me->tuple)) + && 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); + } } diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_irc.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_irc.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_irc.c 2001-12-21 19:42:05.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_irc.c 2002-07-07 00:19:40.000000000 +0200 @@ -51,42 +51,29 @@ /* FIXME: Time out? --RR */ -static int +static unsigned int irc_nat_expected(struct sk_buff **pskb, unsigned int hooknum, struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, unsigned int *verdict) + struct ip_nat_info *info) { struct ip_nat_multi_range mr; u_int32_t newdstip, newsrcip, newip; - struct ip_ct_irc *ircinfo; + + struct ip_conntrack *master = master_ct(ct); IP_NF_ASSERT(info); IP_NF_ASSERT(master); - IP_NF_ASSERT(masterinfo); IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); DEBUGP("nat_expected: We have a connection!\n"); - /* Master must be an irc connection */ - ircinfo = &master->help.ct_irc_info; - LOCK_BH(&ip_irc_lock); - if (ircinfo->is_irc != IP_CONNTR_IRC) { - UNLOCK_BH(&ip_irc_lock); - DEBUGP("nat_expected: master not irc\n"); - return 0; - } - newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; DEBUGP("nat_expected: DCC cmd. %u.%u.%u.%u->%u.%u.%u.%u\n", NIPQUAD(newsrcip), NIPQUAD(newdstip)); - UNLOCK_BH(&ip_irc_lock); - if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) newip = newsrcip; else @@ -99,16 +86,14 @@ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; mr.range[0].min_ip = mr.range[0].max_ip = newip; - *verdict = ip_nat_setup_info(ct, &mr, hooknum); - - return 1; + return ip_nat_setup_info(ct, &mr, hooknum); } -static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info, +static int irc_data_fixup(const struct ip_ct_irc_expect *ct_irc_info, struct ip_conntrack *ct, - unsigned int datalen, struct sk_buff **pskb, - enum ip_conntrack_info ctinfo) + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) { u_int32_t newip; struct ip_conntrack_tuple t; @@ -121,9 +106,9 @@ MUST_BE_LOCKED(&ip_irc_lock); - DEBUGP("IRC_NAT: info (seq %u + %u) packet(seq %u + %u)\n", - ct_irc_info->seq, ct_irc_info->len, - ntohl(tcph->seq), datalen); + DEBUGP("IRC_NAT: info (seq %u + %u) in %u\n", + expect->seq, ct_irc_info->len, + ntohl(tcph->seq)); newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; @@ -133,13 +118,11 @@ only set in ip_conntrack_irc, with ip_irc_lock held writable */ - t = ct->expected.tuple; + t = expect->tuple; t.dst.ip = newip; for (port = ct_irc_info->port; port != 0; port++) { t.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(ct, &t, - &ct->expected.mask, - NULL) == 0) { + if (ip_conntrack_change_expect(expect, &t) == 0) { DEBUGP("using port %d", port); break; } @@ -166,26 +149,28 @@ buffer, NIPQUAD(newip), port); return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, - ct_irc_info->seq - ntohl(tcph->seq), + expect->seq - ntohl(tcph->seq), ct_irc_info->len, buffer, strlen(buffer)); } static unsigned int help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, struct ip_nat_info *info, enum ip_conntrack_info ctinfo, - unsigned int hooknum, struct sk_buff **pskb) + unsigned int hooknum, + struct sk_buff **pskb) { struct iphdr *iph = (*pskb)->nh.iph; struct tcphdr *tcph = (void *) iph + iph->ihl * 4; unsigned int datalen; int dir; - int score; - struct ip_ct_irc *ct_irc_info = &ct->help.ct_irc_info; + struct ip_ct_irc_expect *ct_irc_info; - /* Delete SACK_OK on initial TCP SYNs. */ - if (tcph->syn && !tcph->ack) - ip_nat_delete_sack(*pskb, tcph); + if (!exp) + DEBUGP("ip_nat_irc: no exp!!"); + + ct_irc_info = &exp->help.exp_irc_info; /* Only mangle things once: original direction in POST_ROUTING and reply direction on PRE_ROUTING. */ @@ -202,55 +187,35 @@ DEBUGP("got beyond not touching\n"); datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; - score = 0; LOCK_BH(&ip_irc_lock); - if (ct_irc_info->len) { - DEBUGP("got beyond ct_irc_info->len\n"); - - /* If it's in the right range... */ - score += between(ct_irc_info->seq, ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - score += between(ct_irc_info->seq + ct_irc_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - if (score == 1) { - /* Half a match? This means a partial retransmisison. - It's a cracker being funky. */ - if (net_ratelimit()) { - printk - ("IRC_NAT: partial packet %u/%u in %u/%u\n", - ct_irc_info->seq, ct_irc_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - } + /* Check wether the whole IP/address pattern is carried in the payload */ + if (between(exp->seq + ct_irc_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + if (!irc_data_fixup(ct_irc_info, ct, pskb, ctinfo, exp)) { UNLOCK_BH(&ip_irc_lock); return NF_DROP; - } else if (score == 2) { - DEBUGP("IRC_NAT: score=2, calling fixup\n"); - if (!irc_data_fixup(ct_irc_info, ct, datalen, - pskb, ctinfo)) { - UNLOCK_BH(&ip_irc_lock); - return NF_DROP; - } - /* skb may have been reallocated */ - iph = (*pskb)->nh.iph; - tcph = (void *) iph + iph->ihl * 4; } + } else { + /* Half a match? This means a partial retransmisison. + It's a cracker being funky. */ + if (net_ratelimit()) { + printk + ("IRC_NAT: partial packet %u/%u in %u/%u\n", + exp->seq, ct_irc_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + UNLOCK_BH(&ip_irc_lock); + return NF_DROP; } - UNLOCK_BH(&ip_irc_lock); - ip_nat_seq_adjust(*pskb, ct, ctinfo); - return NF_ACCEPT; } static struct ip_nat_helper ip_nat_irc_helpers[MAX_PORTS]; -static char ip_nih_names[MAX_PORTS][6]; - -static struct ip_nat_expect irc_expect - = { {NULL, NULL}, irc_nat_expected }; - +static char irc_names[MAX_PORTS][10]; /* This function is intentionally _NOT_ defined as __exit, because * it is needed by init() */ @@ -262,52 +227,54 @@ DEBUGP("ip_nat_irc: unregistering helper for port %d\n", ports[i]); ip_nat_helper_unregister(&ip_nat_irc_helpers[i]); - } - ip_nat_expect_unregister(&irc_expect); + } } + static int __init init(void) { - int ret; + int ret = 0; int i; struct ip_nat_helper *hlpr; char *tmpname; - ret = ip_nat_expect_register(&irc_expect); - if (ret == 0) { - - if (ports[0] == 0) { - ports[0] = 6667; - } + if (ports[0] == 0) { + ports[0] = IRC_PORT; + } - for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) { - hlpr = &ip_nat_irc_helpers[i]; - memset(hlpr, 0, - sizeof(struct ip_nat_helper)); - - hlpr->tuple.dst.protonum = IPPROTO_TCP; - hlpr->tuple.src.u.tcp.port = htons(ports[i]); - hlpr->mask.src.u.tcp.port = 0xFFFF; - hlpr->mask.dst.protonum = 0xFFFF; - hlpr->help = help; - - tmpname = &ip_nih_names[i][0]; - sprintf(tmpname, "irc%2.2d", i); - - hlpr->name = tmpname; - DEBUGP - ("ip_nat_irc: Trying to register helper for port %d: name %s\n", - ports[i], hlpr->name); - ret = ip_nat_helper_register(hlpr); - - if (ret) { - printk - ("ip_nat_irc: error registering helper for port %d\n", - ports[i]); - fini(); - return 1; - } - ports_c++; + for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) { + hlpr = &ip_nat_irc_helpers[i]; + memset(hlpr, 0, + sizeof(struct ip_nat_helper)); + + hlpr->tuple.dst.protonum = IPPROTO_TCP; + hlpr->tuple.src.u.tcp.port = htons(ports[i]); + hlpr->mask.src.u.tcp.port = 0xFFFF; + hlpr->mask.dst.protonum = 0xFFFF; + hlpr->help = help; + hlpr->flags = 0; + hlpr->me = THIS_MODULE; + hlpr->expect = irc_nat_expected; + + tmpname = &irc_names[i][0]; + if (ports[i] == IRC_PORT) + sprintf(tmpname, "irc"); + else + sprintf(tmpname, "irc-%d", i); + hlpr->name = tmpname; + + DEBUGP + ("ip_nat_irc: Trying to register helper for port %d: name %s\n", + ports[i], hlpr->name); + ret = ip_nat_helper_register(hlpr); + + if (ret) { + printk + ("ip_nat_irc: error registering helper for port %d\n", + ports[i]); + fini(); + return 1; } + ports_c++; } return ret; } diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_proto_tcp.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_proto_tcp.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_proto_tcp.c 2001-08-07 17:30:50.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_proto_tcp.c 2002-07-07 00:19:40.000000000 +0200 @@ -4,7 +4,6 @@ #include #include #include - #include #include #include diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_proto_unknown.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_proto_unknown.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_proto_unknown.c 2000-03-17 20:56:20.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_proto_unknown.c 2002-07-07 00:19:40.000000000 +0200 @@ -1,5 +1,5 @@ /* The "unknown" protocol. This is what is used for protocols we - * don't understand. It's returned by find_proto(). + * don't understand. It's returned by ip_ct_find_proto(). */ #include diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_rule.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_rule.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_rule.c 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_rule.c 2002-07-07 00:19:40.000000000 +0200 @@ -106,8 +106,6 @@ = { { NULL, NULL }, "nat", &nat_initial_table.repl, NAT_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE }; -LIST_HEAD(nat_expect_list); - /* Source NAT */ static unsigned int ipt_snat_target(struct sk_buff **pskb, unsigned int hooknum, @@ -140,8 +138,12 @@ struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; +#ifdef CONFIG_IP_NF_NAT_LOCAL IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING || hooknum == NF_IP_LOCAL_OUT); +#else + IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING); +#endif ct = ip_conntrack_get(*pskb, &ctinfo); @@ -210,7 +212,7 @@ /* Only allow these for NAT. */ if (strcmp(tablename, "nat") != 0) { - DEBUGP("SNAT: wrong table %s\n", tablename); + DEBUGP("DNAT: wrong table %s\n", tablename); return 0; } @@ -218,6 +220,14 @@ DEBUGP("DNAT: hook mask 0x%x bad\n", hook_mask); return 0; } + +#ifndef CONFIG_IP_NF_NAT_LOCAL + if (hook_mask & (1 << NF_IP_LOCAL_OUT)) { + DEBUGP("DNAT: CONFIG_IP_NF_NAT_LOCAL not enabled\n"); + return 0; + } +#endif + return 1; } @@ -242,19 +252,6 @@ return ip_nat_setup_info(conntrack, &mr, hooknum); } -static inline int call_expect(const struct ip_nat_expect *i, - struct sk_buff **pskb, - unsigned int hooknum, - struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, - unsigned int *verdict) -{ - return i->expect(pskb, hooknum, ct, info, master, masterinfo, - verdict); -} - int ip_nat_rule_find(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, @@ -264,19 +261,8 @@ { int ret; - /* Master won't vanish while this ctrack still alive */ - if (ct->master.master) { - struct ip_conntrack *master; - - master = (struct ip_conntrack *)ct->master.master; - if (LIST_FIND(&nat_expect_list, - call_expect, - struct ip_nat_expect *, - pskb, hooknum, ct, info, - master, &master->nat.info, &ret)) - return ret; - } ret = ipt_do_table(pskb, hooknum, in, out, &nat_table, NULL); + if (ret == NF_ACCEPT) { if (!(info->initialized & (1 << HOOK2MANIP(hooknum)))) /* NUL mapping */ @@ -285,22 +271,6 @@ return ret; } -int ip_nat_expect_register(struct ip_nat_expect *expect) -{ - WRITE_LOCK(&ip_nat_lock); - list_prepend(&nat_expect_list, expect); - WRITE_UNLOCK(&ip_nat_lock); - - return 0; -} - -void ip_nat_expect_unregister(struct ip_nat_expect *expect) -{ - WRITE_LOCK(&ip_nat_lock); - LIST_DELETE(&nat_expect_list, expect); - WRITE_UNLOCK(&ip_nat_lock); -} - static struct ipt_target ipt_snat_reg = { { NULL, NULL }, "SNAT", ipt_snat_target, ipt_snat_checkentry, NULL }; static struct ipt_target ipt_dnat_reg diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_snmp_basic.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_snmp_basic.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_snmp_basic.c 2001-10-31 01:08:12.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_snmp_basic.c 2002-07-07 00:19:40.000000000 +0200 @@ -1243,6 +1243,7 @@ * NAT helper function, packets arrive here from NAT code. */ static unsigned int nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, struct ip_nat_info *info, enum ip_conntrack_info ctinfo, unsigned int hooknum, @@ -1303,19 +1304,27 @@ return NF_DROP; } -static struct ip_nat_helper snmp = { { NULL, NULL }, +static struct ip_nat_helper snmp = { + { NULL, NULL }, + "snmp", + IP_NAT_HELPER_F_STANDALONE, + THIS_MODULE, { { 0, { __constant_htons(SNMP_PORT) } }, { 0, { 0 }, IPPROTO_UDP } }, { { 0, { 0xFFFF } }, { 0, { 0 }, 0xFFFF } }, - nat_help, "snmp" }; + nat_help, NULL }; -static struct ip_nat_helper snmp_trap = { { NULL, NULL }, +static struct ip_nat_helper snmp_trap = { + { NULL, NULL }, + "snmp_trap", + IP_NAT_HELPER_F_STANDALONE, + THIS_MODULE, { { 0, { __constant_htons(SNMP_TRAP_PORT) } }, { 0, { 0 }, IPPROTO_UDP } }, { { 0, { 0xFFFF } }, { 0, { 0 }, 0xFFFF } }, - nat_help, "snmp_trap" }; + nat_help, NULL }; /***************************************************************************** * diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_standalone.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_standalone.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_standalone.c 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_standalone.c 2002-07-07 00:19:40.000000000 +0200 @@ -5,7 +5,12 @@ */ /* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General - Public Licence. */ + * Public Licence. + * + * 23 Apr 2001: Harald Welte + * - new API and handling of conntrack/nat helpers + * - now capable of multiple expectations for one master + * */ #include #include @@ -41,7 +46,17 @@ #define HOOKNAME(hooknum) ((hooknum) == NF_IP_POST_ROUTING ? "POST_ROUTING" \ : ((hooknum) == NF_IP_PRE_ROUTING ? "PRE_ROUTING" \ : ((hooknum) == NF_IP_LOCAL_OUT ? "LOCAL_OUT" \ - : "*ERROR*"))) + : ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN" \ + : "*ERROR*"))) + +static inline int call_expect(struct ip_conntrack *master, + struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + return master->nat.info.helper->expect(pskb, hooknum, ct, info); +} static unsigned int ip_nat_fn(unsigned int hooknum, @@ -94,6 +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); @@ -103,8 +124,16 @@ int in_hashes = info->initialized; unsigned int ret; - ret = ip_nat_rule_find(pskb, hooknum, in, out, - ct, info); + if (ct->master + && master_ct(ct)->nat.info.helper + && master_ct(ct)->nat.info.helper->expect) { + ret = call_expect(master_ct(ct), pskb, + hooknum, ct, info); + } else { + ret = ip_nat_rule_find(pskb, hooknum, in, out, + ct, info); + } + if (ret != NF_ACCEPT) { WRITE_UNLOCK(&ip_nat_lock); return ret; @@ -204,6 +233,11 @@ 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 +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. */ int ip_nat_protocol_register(struct ip_nat_protocol *proto) { @@ -272,6 +306,13 @@ 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"); + goto cleanup_localoutops; + } +#endif if (ip_conntrack_module) __MOD_INC_USE_COUNT(ip_conntrack_module); return ret; @@ -279,6 +320,10 @@ cleanup: if (ip_conntrack_module) __MOD_DEC_USE_COUNT(ip_conntrack_module); +#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: nf_unregister_hook(&ip_nat_out_ops); @@ -307,12 +352,11 @@ module_exit(fini); EXPORT_SYMBOL(ip_nat_setup_info); +EXPORT_SYMBOL(ip_nat_protocol_register); +EXPORT_SYMBOL(ip_nat_protocol_unregister); EXPORT_SYMBOL(ip_nat_helper_register); EXPORT_SYMBOL(ip_nat_helper_unregister); -EXPORT_SYMBOL(ip_nat_expect_register); -EXPORT_SYMBOL(ip_nat_expect_unregister); EXPORT_SYMBOL(ip_nat_cheat_check); EXPORT_SYMBOL(ip_nat_mangle_tcp_packet); -EXPORT_SYMBOL(ip_nat_seq_adjust); -EXPORT_SYMBOL(ip_nat_delete_sack); +EXPORT_SYMBOL(ip_nat_used_tuple); MODULE_LICENSE("GPL"); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_talk.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_talk.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_nat_talk.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_nat_talk.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,473 @@ +/* + * talk extension for UDP NAT alteration. + * Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + ** + * Module load syntax: + * insmod ip_nat_talk.o talk=[0|1] ntalk=[0|1] ntalk2=[0|1] + * + * talk=[0|1] disable|enable old talk support + * ntalk=[0|1] disable|enable ntalk support + * ntalk2=[0|1] disable|enable ntalk2 support + * + * The default is talk=1 ntalk=1 ntalk2=1 + * + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Default all talk protocols are supported */ +static int talk = 1; +static int ntalk = 1; +static int ntalk2 = 1; +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("talk network address translation module"); +#ifdef MODULE_PARM +MODULE_PARM(talk, "i"); +MODULE_PARM_DESC(talk, "support (old) talk protocol"); +MODULE_PARM(ntalk, "i"); +MODULE_PARM_DESC(ntalk, "support ntalk protocol"); +MODULE_PARM(ntalk2, "i"); +MODULE_PARM_DESC(ntalk2, "support ntalk2 protocol"); +#endif + +#if 0 +#define DEBUGP printk +#define IP_NAT_TALK_DEBUG +#else +#define DEBUGP(format, args...) +#endif + +/* FIXME: Time out? --RR */ + +static int +mangle_packet(struct sk_buff **pskb, + struct ip_conntrack *ct, + u_int32_t newip, + u_int16_t port, + struct talk_addr *addr, + struct talk_addr *ctl_addr) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + size_t udplen = (*pskb)->len - iph->ihl * 4; + + /* Fortunately talk sends a structure with the address and + port in it. The size of the packet won't change. */ + + if (ctl_addr == NULL) { + /* response */ + if (addr->ta_addr == INADDR_ANY) + return 1; + DEBUGP("ip_nat_talk_mangle_packet: response orig %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + NIPQUAD(newip), ntohs(port)); + addr->ta_addr = newip; + addr->ta_port = port; + } else { + /* message */ + if (addr->ta_addr != INADDR_ANY) { + /* Change address inside packet to match way we're mapping + this connection. */ + DEBUGP("ip_nat_talk_mangle_packet: message orig addr %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), + ntohs(addr->ta_port)); + addr->ta_addr = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + } + DEBUGP("ip_nat_talk_mangle_packet: message orig ctl_addr %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", + NIPQUAD(ctl_addr->ta_addr), ntohs(ctl_addr->ta_port), + NIPQUAD(newip), ntohs(port)); + ctl_addr->ta_addr = newip; + ctl_addr->ta_port = port; + } + + /* Fix checksums */ + (*pskb)->csum = csum_partial((char *)udph + sizeof(struct udphdr), udplen - sizeof(struct udphdr), 0); + udph->check = 0; + udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + csum_partial((char *)udph, sizeof(struct udphdr), (*pskb)->csum)); + + ip_send_check(iph); + return 1; +} + +static int talk_help_msg(struct ip_conntrack *ct, + struct sk_buff **pskb, + u_char type, + struct talk_addr *addr, + struct talk_addr *ctl_addr) +{ + u_int32_t newip; + u_int16_t port; + + unsigned int verdict = NF_ACCEPT; + + DEBUGP("ip_nat_talk_help_msg: addr: %u.%u.%u.%u:%u, ctl_addr: %u.%u.%u.%u:%u, type %d\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + NIPQUAD(ctl_addr->ta_addr), ntohs(ctl_addr->ta_port), + type); + + /* Change address inside packet to match way we're mapping + this connection. */ + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port; + DEBUGP("ip_nat_talk_help_msg: inserting: %u.%u.%u.%u:%u\n", + NIPQUAD(newip), ntohs(port)); + + if (!mangle_packet(pskb, ct, newip, port, addr, ctl_addr)) + verdict = NF_DROP; + + return verdict; +} + +static int talk_help_response(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct sk_buff **pskb, + u_char type, + u_char answer, + struct talk_addr *addr) +{ + u_int32_t newip; + u_int16_t port; + struct ip_conntrack_tuple t; + struct ip_ct_talk_expect *ct_talk_info; + + DEBUGP("ip_nat_talk_help_response: addr: %u.%u.%u.%u:%u, type %d answer %d\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + type, answer); + + LOCK_BH(&ip_talk_lock); + ct_talk_info = &exp->help.exp_talk_info; + + if (!(answer == SUCCESS + && (type == LOOK_UP || type == ANNOUNCE) + && exp != NULL)) { + UNLOCK_BH(&ip_talk_lock); + return NF_ACCEPT; + } + + DEBUGP("ip_nat_talk_help_response: talkinfo port %u (%s)\n", + ntohs(ct_talk_info->port), + type == LOOK_UP ? "LOOK_UP" : "ANNOUNCE"); + + /* Change address inside packet to match way we're mapping + this connection. */ + newip = ct->tuplehash[type == LOOK_UP ? IP_CT_DIR_ORIGINAL : + IP_CT_DIR_REPLY].tuple.dst.ip; + /* We can read expect here without conntrack lock, since it's + only set in ip_conntrack_talk , with ip_talk_lock held + writable */ + t = exp->tuple; + t.dst.ip = newip; + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(ct_talk_info->port); port != 0; port++) { + if (type == LOOK_UP) + t.dst.u.tcp.port = htons(port); + else + t.dst.u.udp.port = htons(port); + + if (ip_conntrack_change_expect(exp, &t) == 0) { + DEBUGP("ip_nat_talk_help_response: using %u.%u.%u.%u:%u\n", NIPQUAD(newip), port); + break; + } + } + UNLOCK_BH(&ip_talk_lock); + + if (port == 0 || !mangle_packet(pskb, ct, newip, htons(port), addr, NULL)) + return NF_DROP; + + return NF_ACCEPT; +} + +static unsigned int talk_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb, + int talk_port) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + unsigned int udplen = (*pskb)->len - iph->ihl * 4; + char *data = (char *)udph + sizeof(struct udphdr); + int dir; + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + dir = CTINFO2DIR(ctinfo); + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("ip_nat_talk_help: Not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + return NF_ACCEPT; + } + DEBUGP("ip_nat_talk_help: dir %s at hook %s, %u.%u.%u.%u:%u->%u.%u.%u.%u:%u, talk port %d\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???", + NIPQUAD(iph->saddr), ntohs(udph->source), + NIPQUAD(iph->daddr), ntohs(udph->dest), + talk_port); + + /* Because conntrack does not drop packets, checking must be repeated here... */ + if (talk_port == TALK_PORT) { + if (dir == IP_CT_DIR_ORIGINAL + && udplen == sizeof(struct udphdr) + sizeof(struct talk_msg)) + return talk_help_msg(ct, pskb, + ((struct talk_msg *)data)->type, + &(((struct talk_msg *)data)->addr), + &(((struct talk_msg *)data)->ctl_addr)); + else if (dir == IP_CT_DIR_REPLY + && udplen == sizeof(struct udphdr) + sizeof(struct talk_response)) + return talk_help_response(ct, exp, pskb, + ((struct talk_response *)data)->type, + ((struct talk_response *)data)->answer, + &(((struct talk_response *)data)->addr)); + else { + DEBUGP("ip_nat_talk_help: not talk %s, datalen %u != %u\n", + dir == IP_CT_DIR_ORIGINAL ? "message" : "response", + (unsigned)udplen - sizeof(struct udphdr), + dir == IP_CT_DIR_ORIGINAL ? sizeof(struct talk_msg) : sizeof(struct talk_response)); + return NF_DROP; + } + } else { + if (dir == IP_CT_DIR_ORIGINAL) { + if (ntalk + && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_msg) + && ((struct ntalk_msg *)data)->vers == NTALK_VERSION) + return talk_help_msg(ct, pskb, + ((struct ntalk_msg *)data)->type, + &(((struct ntalk_msg *)data)->addr), + &(((struct ntalk_msg *)data)->ctl_addr)); + else if (ntalk2 + && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_msg) + && ((struct ntalk2_msg *)data)->vers == NTALK2_VERSION + && udplen == sizeof(struct udphdr) + + sizeof(struct ntalk2_msg) + + ((struct ntalk2_msg *)data)->extended) + return talk_help_msg(ct, pskb, + ((struct ntalk2_msg *)data)->type, + &(((struct ntalk2_msg *)data)->addr), + &(((struct ntalk2_msg *)data)->ctl_addr)); + else { + DEBUGP("ip_nat_talk_help: not ntalk/ntalk2 message, datalen %u != %u or %u + max 256\n", + (unsigned)udplen - sizeof(struct udphdr), + sizeof(struct ntalk_msg), sizeof(struct ntalk2_msg)); + return NF_DROP; + } + } else { + if (ntalk + && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_response) + && ((struct ntalk_response *)data)->vers == NTALK_VERSION) + return talk_help_response(ct, exp, pskb, + ((struct ntalk_response *)data)->type, + ((struct ntalk_response *)data)->answer, + &(((struct ntalk_response *)data)->addr)); + else if (ntalk2 + && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_response) + && ((struct ntalk2_response *)data)->vers == NTALK2_VERSION) + return talk_help_response(ct, exp, pskb, + ((struct ntalk2_response *)data)->type, + ((struct ntalk2_response *)data)->answer, + &(((struct ntalk2_response *)data)->addr)); + else { + DEBUGP("ip_nat_talk_help: not ntalk/ntalk2 response, datalen %u != %u or %u + max 256\n", + (unsigned)udplen - sizeof(struct udphdr), + sizeof(struct ntalk_response), sizeof(struct ntalk2_response)); + return NF_DROP; + } + } + } +} + +static unsigned int help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + return talk_help(ct, exp, info, ctinfo, hooknum, pskb, TALK_PORT); +} + +static unsigned int nhelp(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + return talk_help(ct, exp, info, ctinfo, hooknum, pskb, NTALK_PORT); +} + +static unsigned int +talk_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info); + +static struct ip_nat_helper talk_helpers[2] = + { { { NULL, NULL }, + "talk", /* name */ + IP_NAT_HELPER_F_ALWAYS, /* flags */ + THIS_MODULE, /* module */ + { { 0, { __constant_htons(TALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + help, /* helper */ + talk_nat_expected }, /* expectfn */ + { { NULL, NULL }, + "ntalk", /* name */ + IP_NAT_HELPER_F_ALWAYS, /* flags */ + THIS_MODULE, /* module */ + { { 0, { __constant_htons(NTALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + nhelp, /* helper */ + talk_nat_expected } /* expectfn */ + }; + +static unsigned int +talk_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + struct ip_nat_multi_range mr; + u_int32_t newdstip, newsrcip, newip; + u_int16_t port; + unsigned int ret; + + struct ip_conntrack *master = master_ct(ct); + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + + IP_NF_ASSERT(!(info->initialized & (1<master->help.exp_talk_info.port; + UNLOCK_BH(&ip_talk_lock); + + DEBUGP("ip_nat_talk_expected: dir %s at hook %s, ct %p, master %p\n", + CTINFO2DIR((*pskb)->nfct - ct->infos) == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???", + ct, master); + + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) { + /* Callee client -> caller server */ +#ifdef IP_NAT_TALK_DEBUG + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + + DEBUGP("ip_nat_talk_expected: UDP %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(udph->source), + NIPQUAD(iph->daddr), ntohs(udph->dest)); +#endif + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + DEBUGP("ip_nat_talk_expected: callee client -> caller server, newsrc: %u.%u.%u.%u, newdst: %u.%u.%u.%u\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } else { + /* Callee client -> caller client */ +#ifdef IP_NAT_TALK_DEBUG + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + + DEBUGP("ip_nat_talk_expected: TCP %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), + NIPQUAD(iph->daddr), ntohs(tcph->dest)); +#endif + newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + DEBUGP("ip_nat_talk_expected: callee client -> caller client, newsrc: %u.%u.%u.%u, newdst: %u.%u.%u.%u\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) + newip = newsrcip; + else + newip = newdstip; + + DEBUGP("ip_nat_talk_expected: IP to %u.%u.%u.%u, port %u\n", NIPQUAD(newip), ntohs(port)); + + mr.rangesize = 1; + /* We don't want to manip the per-protocol, just the IPs... */ + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + /* ... unless we're doing a MANIP_DST, in which case, make + sure we map to the correct port */ + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { + mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + mr.range[0].min = mr.range[0].max + = ((union ip_conntrack_manip_proto) + { port }); + } + ret = ip_nat_setup_info(ct, &mr, hooknum); + + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) { + DEBUGP("talk_expected: setting NAT helper for %p\n", ct); + /* NAT expectfn called with ip_nat_lock write-locked */ + info->helper = &talk_helpers[htons(port) - TALK_PORT]; + } + return ret; +} + +static int __init init(void) +{ + int ret = 0; + + if (talk > 0) { + ret = ip_nat_helper_register(&talk_helpers[0]); + + if (ret != 0) + return ret; + } + if (ntalk > 0 || ntalk2 > 0) { + ret = ip_nat_helper_register(&talk_helpers[1]); + + if (ret != 0 && talk > 0) + ip_nat_helper_unregister(&talk_helpers[0]); + } + return ret; +} + +static void __exit fini(void) +{ + if (talk > 0) + ip_nat_helper_unregister(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_nat_helper_unregister(&talk_helpers[1]); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ip_pool.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_pool.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ip_pool.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ip_pool.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,332 @@ +/* Kernel module for IP pool management */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DP printk +#else +#define DP(format, args...) +#endif + +MODULE_LICENSE("GPL"); + +#define NR_POOL 16 +static int nr_pool = NR_POOL;/* overwrite this when loading module */ + +struct ip_pool { + u_int32_t first_ip; /* host byte order, included in range */ + u_int32_t last_ip; /* host byte order, included in range */ + void *members; /* the bitmap proper */ + int nr_use; /* total nr. of tests through this */ + int nr_match; /* total nr. of matches through this */ + rwlock_t lock; +}; + +static struct ip_pool *POOL; + +static inline struct ip_pool *lookup(ip_pool_t index) +{ + if (index < 0 || index >= nr_pool) { + DP("ip_pool:lookup: bad index %d\n", index); + return 0; + } + return POOL+index; +} + +int ip_pool_match(ip_pool_t index, u_int32_t addr) +{ + struct ip_pool *pool = lookup(index); + int res = 0; + + if (!pool || !pool->members) + return 0; + read_lock_bh(&pool->lock); + if (pool->members) { + if (addr >= pool->first_ip && addr <= pool->last_ip) { + addr -= pool->first_ip; + if (test_bit(addr, pool->members)) { + res = 1; +#ifdef CONFIG_IP_POOL_STATISTICS + pool->nr_match++; +#endif + } + } +#ifdef CONFIG_IP_POOL_STATISTICS + pool->nr_use++; +#endif + } + read_unlock_bh(&pool->lock); + return res; +} + +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; imembers) + return -EBADF; + req.addr = htonl(pool->first_ip); + req.addr2 = htonl(pool->last_ip); + return copy_to_user(user, &req, sizeof(req)); + case IP_POOL_USAGE: + DP("ip_pool USE\n"); + pool = lookup(req.index); + if (!pool) + return -EINVAL; + if (!pool->members) + return -EBADF; + req.addr = pool->nr_use; + req.addr2 = pool->nr_match; + return copy_to_user(user, &req, sizeof(req)); + case IP_POOL_TEST_ADDR: + DP("ip_pool TEST 0x%08x\n", req.addr); + pool = lookup(req.index); + if (!pool) + return -EINVAL; + res = 0; + read_lock_bh(&pool->lock); + if (!pool->members) { + DP("ip_pool TEST_ADDR no members in pool\n"); + res = -EBADF; + goto unlock_and_return_res; + } + req.addr = ntohl(req.addr); + if (req.addr < pool->first_ip) { + DP("ip_pool TEST_ADDR address < pool bounds\n"); + res = -ERANGE; + goto unlock_and_return_res; + } + if (req.addr > pool->last_ip) { + DP("ip_pool TEST_ADDR address > pool bounds\n"); + res = -ERANGE; + goto unlock_and_return_res; + } + req.addr = (0 != test_bit((req.addr - pool->first_ip), + pool->members)); + read_unlock_bh(&pool->lock); + return copy_to_user(user, &req, sizeof(req)); + case IP_POOL_FLUSH: + DP("ip_pool FLUSH not yet implemented.\n"); + return -EBUSY; + case IP_POOL_DESTROY: + DP("ip_pool DESTROY not yet implemented.\n"); + return -EBUSY; + case IP_POOL_INIT: + DP("ip_pool INIT 0x%08x-0x%08x\n", req.addr, req.addr2); + pool = lookup(req.index); + if (!pool) + return -EINVAL; + req.addr = ntohl(req.addr); + req.addr2 = ntohl(req.addr2); + if (req.addr > req.addr2) { + DP("ip_pool INIT bad ip range\n"); + return -EINVAL; + } + newbytes = bitmap_bytes(req.addr, req.addr2); + newmembers = kmalloc(newbytes, GFP_KERNEL); + if (!newmembers) { + DP("ip_pool INIT out of mem for %d bytes\n", newbytes); + return -ENOMEM; + } + memset(newmembers, 0, newbytes); + write_lock_bh(&pool->lock); + if (pool->members) { + DP("ip_pool INIT pool %d exists\n", req.index); + kfree(newmembers); + res = -EBUSY; + goto unlock_and_return_res; + } + pool->first_ip = req.addr; + pool->last_ip = req.addr2; + pool->nr_use = 0; + pool->nr_match = 0; + pool->members = newmembers; + write_unlock_bh(&pool->lock); + return 0; + case IP_POOL_ADD_ADDR: + DP("ip_pool ADD_ADDR 0x%08x\n", req.addr); + req.addr = pool_change(req.index, ntohl(req.addr), 0); + return copy_to_user(user, &req, sizeof(req)); + case IP_POOL_DEL_ADDR: + DP("ip_pool DEL_ADDR 0x%08x\n", req.addr); + req.addr = pool_change(req.index, ntohl(req.addr), 1); + return copy_to_user(user, &req, sizeof(req)); + default: + DP("ip_pool:getpool bad op %d\n", req.op); + return -EINVAL; + } + return -EINVAL; + +unlock_and_return_res: + if (pool) + read_unlock_bh(&pool->lock); + return res; +} + +static struct nf_sockopt_ops so_pool += { { NULL, NULL }, PF_INET, + SO_IP_POOL, SO_IP_POOL+1, &setpool, + SO_IP_POOL, SO_IP_POOL+1, &getpool, + 0, NULL }; + +MODULE_PARM(nr_pool, "i"); + +static int __init init(void) +{ + ip_pool_t i; + int res; + + if (nr_pool < 1) { + printk("ip_pool module init: bad nr_pool %d\n", nr_pool); + return -EINVAL; + } + POOL = kmalloc(nr_pool * sizeof(*POOL), GFP_KERNEL); + if (!POOL) { + printk("ip_pool module init: out of memory for nr_pool %d\n", + nr_pool); + return -ENOMEM; + } + for (i=0; ipeer.pid, MSG_DONTWAIT); } -#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0); +#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) static __inline__ void netlink_receive_user_skb(struct sk_buff *skb) { diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_DSCP.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_DSCP.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_DSCP.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_DSCP.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,108 @@ +/* iptables module for setting the IPv4 DSCP field, Version 1.5 + * + * (C) 2002 by Harald Welte + * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh + * This software is distributed under GNU GPL v2, 1991 + * + * See RFC2474 for a description of the DSCP field within the IP Header. + * + * ipt_DSCP.c,v 1.5 2002/02/17 21:11:08 laforge Exp +*/ + +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("IP tables DSCP modification module"); +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; + const struct ipt_DSCP_info *dinfo = targinfo; + u_int8_t sh_dscp = ((dinfo->dscp << IPT_DSCP_SHIFT) & IPT_DSCP_MASK); + + + if ((iph->tos & IPT_DSCP_MASK) != sh_dscp) { + u_int16_t diffs[2]; + + /* raw socket (tcpdump) may have clone of incoming + * skb: don't disturb it --RR */ + if (skb_cloned(*pskb) && !(*pskb)->sk) { + struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); + if (!nskb) + return NF_DROP; + kfree_skb(*pskb); + *pskb = nskb; + iph = (*pskb)->nh.iph; + } + + diffs[0] = htons(iph->tos) ^ 0xFFFF; + iph->tos = iph->tos | sh_dscp; + diffs[1] = htons(iph->tos); + iph->check = csum_fold(csum_partial((char *)diffs, + sizeof(diffs), + iph->check^0xFFFF)); + (*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) +{ + const u_int8_t dscp = ((struct ipt_DSCP_info *)targinfo)->dscp; + + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_DSCP_info))) { + printk(KERN_WARNING "DSCP: targinfosize %u != %Zu\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_DSCP_info))); + return 0; + } + + if (strcmp(tablename, "mangle") != 0) { + printk(KERN_WARNING "DSCP: can only be called from \"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + + if ((dscp > IPT_DSCP_MAX)) { + printk(KERN_WARNING "DSCP: dscp %x out of range\n", dscp); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_dscp_reg += { { NULL, NULL }, "DSCP", target, checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_target(&ipt_dscp_reg)) + return -EINVAL; + + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_dscp_reg); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,84 @@ +/** + * Strip all IP options in the IP packet header. + * + * (C) 2001 by Fabrice MARIE + * This software is distributed under GNU GPL v2, 1991 + */ + +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Fabrice MARIE "); +MODULE_DESCRIPTION("Stip 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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_NETLINK.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_NETLINK.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_NETLINK.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_NETLINK.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Gianni Tedesco "); +MODULE_DESCRIPTION("Provides iptables NETLINK target similar to ipchains -o"); +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static struct sock *ipfwsk; + +static unsigned int ipt_netlink_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, void *userinfo) +{ + struct ipt_nldata *nld = (struct ipt_nldata *)targinfo; + struct iphdr *ip = (*pskb)->nh.iph; + struct sk_buff *outskb; + struct netlink_t nlhdr; + size_t len=0; + + /* Allocate a socket buffer */ + if ( MASK(nld->flags, USE_SIZE) ) + len = nld->size+sizeof(nlhdr); + else + len = ntohs(ip->tot_len)+sizeof(nlhdr); + + outskb=alloc_skb(len, GFP_ATOMIC); + + if (outskb) { + nlhdr.len=len; + + if ( MASK(nld->flags, USE_MARK) ) + nlhdr.mark=(*pskb)->nfmark=nld->mark; + else + nlhdr.mark=(*pskb)->nfmark; + + if ( in && in->name ) { + strncpy((char *)&nlhdr.iface, in->name, IFNAMSIZ); + }else if ( out && out->name ){ + strncpy((char *)&nlhdr.iface, out->name, IFNAMSIZ); + } + + skb_put(outskb, len); + memcpy(outskb->data, &nlhdr, sizeof(nlhdr)); + memcpy((outskb->data)+sizeof(nlhdr), ip, len-sizeof(nlhdr)); + netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_KERNEL); + }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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_NETMAP.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_NETMAP.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_NETMAP.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_NETMAP.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,107 @@ +/* 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 +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define MODULENAME "NETMAP" +MODULE_LICENSE("GPL"); + +#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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_POOL.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_POOL.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_POOL.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_POOL.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,116 @@ +/* ipt_POOL.c - netfilter target to manipulate IP pools + * + * This target can be used almost everywhere. It acts on some specified + * IP pool, adding or deleting some IP address in the pool. The address + * can be either the source (--addsrc, --delsrc), or destination (--add/deldst) + * of the packet under inspection. + * + * The target normally returns IPT_CONTINUE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/*** NOTE NOTE NOTE NOTE *** +** +** By sheer luck, I get away with using the "struct ipt_pool_info", as defined +** in , both as the match and target info. +** Here, in the target implementation, ipt_pool_info.src, if not IP_POOL_NONE, +** is modified for the source IP address of the packet under inspection. +** The same way, the ipt_pool_info.dst pool is modified for the destination. +** +** The address is added to the pool normally. However, if IPT_POOL_DEL_dir +** flag is set in ipt_pool_info.flags, the address is deleted from the pool. +** +** If a modification was done to the pool, we possibly return ACCEPT or DROP, +** if the right IPT_POOL_MOD_dir_ACCEPT or _MOD_dir_DROP flags are set. +** The IPT_POOL_INV_MOD_dir flag inverts the sense of the check (i.e. the +** ACCEPT and DROP flags are evaluated when the pool was not modified.) +*/ + +static int +do_check(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + const struct ipt_pool_info *ipi = targinfo; + + if (targinfosize != IPT_ALIGN(sizeof(*ipi))) { + DEBUGP("POOL_check: size %u.\n", targinfosize); + return 0; + } + DEBUGP("ipt_POOL:do_check(%d,%d,%d)\n",ipi->src,ipi->dst,ipi->flags); + return 1; +} + +static unsigned int +do_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ipt_pool_info *ipi = targinfo; + int modified; + unsigned int verdict = IPT_CONTINUE; + + if (ipi->src != IP_POOL_NONE) { + modified = ip_pool_mod(ipi->src, ntohl((*pskb)->nh.iph->saddr), + ipi->flags & IPT_POOL_DEL_SRC); + if (!!modified ^ !!(ipi->flags & IPT_POOL_INV_MOD_SRC)) { + if (ipi->flags & IPT_POOL_MOD_SRC_ACCEPT) + verdict = NF_ACCEPT; + else if (ipi->flags & IPT_POOL_MOD_SRC_DROP) + verdict = NF_DROP; + } + } + if (verdict == IPT_CONTINUE && ipi->dst != IP_POOL_NONE) { + modified = ip_pool_mod(ipi->dst, ntohl((*pskb)->nh.iph->daddr), + ipi->flags & IPT_POOL_DEL_DST); + if (!!modified ^ !!(ipi->flags & IPT_POOL_INV_MOD_DST)) { + if (ipi->flags & IPT_POOL_MOD_DST_ACCEPT) + verdict = NF_ACCEPT; + else if (ipi->flags & IPT_POOL_MOD_DST_DROP) + verdict = NF_DROP; + } + } + return verdict; +} + +static struct ipt_target pool_reg += { { NULL, NULL }, "POOL", do_target, do_check, NULL, THIS_MODULE }; + +static int __init init(void) +{ + DEBUGP("init ipt_POOL\n"); + return ipt_register_target(&pool_reg); +} + +static void __exit fini(void) +{ + DEBUGP("fini ipt_POOL\n"); + ipt_unregister_target(&pool_reg); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_REJECT.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_REJECT.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_REJECT.c 2001-10-06 17:50:28.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_REJECT.c 2002-07-07 00:19:40.000000000 +0200 @@ -1,6 +1,7 @@ /* * This is a module which is used for rejecting packets. * Added support for customized reject packets (Jozsef Kadlecsik). + * Added support for fake source IP in icmp-unreach (Fabrice MARIE). */ #include #include @@ -150,7 +151,7 @@ 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_int32_t fake_source_address) { struct iphdr *iph; struct icmphdr *icmph; @@ -234,16 +235,18 @@ iph->tos=tos; iph->tot_len = htons(length); - /* This abbreviates icmp->send->ip_build_xmit->ip_dont_fragment */ - if (!ipv4_config.no_pmtu_disc - && !(rt->u.dst.mxlock&(1<frag_off = htons(IP_DF); - else iph->frag_off = 0; + /* PMTU discovery never applies to ICMP packets. */ + iph->frag_off = 0; 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 != 0) + iph->saddr=fake_source_address; + else + iph->saddr=rt->rt_src; iph->daddr=rt->rt_dst; iph->check=0; iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); @@ -290,22 +293,22 @@ 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_TCP_RESET: send_reset(*pskb, hooknum == NF_IP_LOCAL_IN); @@ -352,6 +355,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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_SAME.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_SAME.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_SAME.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_SAME.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,199 @@ +/* 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 + * * copied ipt_BALANCE.c to ipt_SAME.c and changed a few things. + * 010728 Martin Josefsson + * * added --nodst to not include destination-ip in new source + * calculations. + * * added some more sanity-checks. + * 010729 Martin Josefsson + * * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +#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_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_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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_TTL.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_TTL.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_TTL.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_TTL.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,110 @@ +/* TTL modification target for IP tables + * (C) 2000 by Harald Welte + * + * Version: 1.8 + * + * This software is distributed under the terms of GNU GPL + */ + +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("IP tables TTL modification module"); +MODULE_LICENSE("GPL"); + +static unsigned int ipt_ttl_target(struct sk_buff **pskb, unsigned int hooknum, + const struct net_device *in, const struct net_device *out, + const void *targinfo, void *userinfo) +{ + struct iphdr *iph = (*pskb)->nh.iph; + const struct ipt_TTL_info *info = targinfo; + u_int16_t diffs[2]; + int new_ttl; + + switch (info->mode) { + case IPT_TTL_SET: + new_ttl = info->ttl; + break; + case IPT_TTL_INC: + new_ttl = iph->ttl + info->ttl; + if (new_ttl > 255) + new_ttl = 255; + break; + case IPT_TTL_DEC: + new_ttl = iph->ttl + info->ttl; + if (new_ttl < 0) + new_ttl = 0; + break; + default: + new_ttl = iph->ttl; + break; + } + + if (new_ttl != iph->ttl) { + diffs[0] = htons(((unsigned)iph->ttl) << 8) ^ 0xFFFF; + iph->ttl = new_ttl; + diffs[1] = htons(((unsigned)iph->ttl) << 8); + iph->check = csum_fold(csum_partial((char *)diffs, + sizeof(diffs), + iph->check^0xFFFF)); + (*pskb)->nfcache |= NFC_ALTERED; + } + + return IPT_CONTINUE; +} + +static int ipt_ttl_checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + struct ipt_TTL_info *info = targinfo; + + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_TTL_info))) { + printk(KERN_WARNING "TTL: targinfosize %u != %Zu\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_TTL_info))); + return 0; + } + + if (strcmp(tablename, "mangle")) { + printk(KERN_WARNING "TTL: can only be called from \"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + + if (info->mode > IPT_TTL_MAXMODE) { + printk(KERN_WARNING "TTL: invalid or unknown Mode %u\n", + info->mode); + return 0; + } + + if ((info->mode != IPT_TTL_SET) && (info->ttl == 0)) { + printk(KERN_WARNING "TTL: increment/decrement doesn't make sense with value 0\n"); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_TTL = { { NULL, NULL }, "TTL", + ipt_ttl_target, ipt_ttl_checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_target(&ipt_TTL); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_TTL); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_ULOG.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_ULOG.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_ULOG.c 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_ULOG.c 2002-07-07 00:19:40.000000000 +0200 @@ -29,7 +29,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.15 2002/01/18 21:33:19 laforge Exp + * ipt_ULOG.c,v 1.18 2002/04/16 07:33:00 laforge Exp */ #include @@ -61,7 +61,7 @@ #define DEBUGP(format, args...) #endif -#define PRINTR(format, args...) do { if (net_ratelimit()) printk(format, ## args); } while (0); +#define PRINTR(format, args...) do { if (net_ratelimit()) printk(format, ## args); } while (0) MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("IP tables userspace logging module"); @@ -339,10 +339,28 @@ static void __exit fini(void) { + ulog_buff_t *ub; + int i; + DEBUGP("ipt_ULOG: cleanup_module\n"); ipt_unregister_target(&ipt_ulog_reg); sock_release(nflognl->socket); + + /* remove pending timers and free allocated skb's */ + for (i = 0; i < ULOG_MAXNLGROUPS; i++) { + ub = &ulog_buffers[i]; + if (timer_pending(&ub->timer)) { + DEBUGP("timer was pending, deleting\n"); + del_timer(&ub->timer); + } + + if (ub->skb) { + kfree_skb(ub->skb); + ub->skb = NULL; + } + } + } module_init(init); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_bytelimit.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_bytelimit.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_bytelimit.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_bytelimit.c 2002-07-07 00:20:46.000000000 +0200 @@ -0,0 +1,106 @@ +/* Kernel module to control the rate + * + * Jérôme de Vivie + * Hervé Eychenne + * + * 2 September 1999: Changed from the target RATE to the match + * `limit', removed logging. Did I mention that + * Alexey is a fucking genius? + * Rusty Russell (rusty@rustcorp.com.au). + * 19 November 2001: shamelessly cloned from ipt_limit.c + * Jerome Petazzoni + */ + +#include +#include +#include +#include + +#include +#include + +static spinlock_t limit_lock = SPIN_LOCK_UNLOCKED; + +/* + Skaya: I modified all the limit stuff. Now, you get "tickrate" credits per + jiffy, and passing X bytes costs you X credits. "credit_cap" is + now "burst", which is the maximal credit you can ever have. + + "prev" tracks the last packet hit: you gain "rate" credits per jiffy. + If you get credit balance more than this, the extra credit is + discarded. +*/ + +static int +ipt_bytelimit_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_bytelimitinfo *r = + ((struct ipt_bytelimitinfo *)matchinfo)->master; + unsigned long now = jiffies; + + spin_lock_bh(&limit_lock); + r->credit += (now - xchg(&r->prev, now)) * r->tickrate ; + if (r->credit > r->quota) + r->credit = r->quota ; + + if (r->credit >= datalen) { + /* We're not limited. */ + r->credit -= datalen; + spin_unlock_bh(&limit_lock); + return 0; + } + + spin_unlock_bh(&limit_lock); + return 1; +} + +static int +ipt_bytelimit_checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_bytelimitinfo *r = matchinfo; + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_bytelimitinfo))) + return 0; + + if (r->credit==-1) r->credit = r->quota ; + r->prev = jiffies; + r->tickrate = r->rate/HZ ; + r->rate = r->tickrate*HZ ; /* so we display the "real", rounded, rate */ + + /* For SMP, we only want to use one set of counters. */ + r->master = r; + + return 1; +} + +static struct ipt_match ipt_bytelimit_reg += { { NULL, NULL }, "bytelimit", + ipt_bytelimit_match, ipt_bytelimit_checkentry, NULL, + THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_match(&ipt_bytelimit_reg)) + return -EINVAL; + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipt_bytelimit_reg); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_conntrack.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_conntrack.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_conntrack.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_conntrack.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,123 @@ +/* Kernel module to match connection tracking information. + * Superset of Rusty's minimalistic state match. + * GPL (C) 2001 Marc Boucher (marc@mbsi.ca). + */ +#include +#include +#include +#include +#include + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_conntrack_info *sinfo = matchinfo; + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + unsigned int statebit; + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + +#define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg)) + + statebit = ct ? IPT_CONNTRACK_STATE_INVALID : IPT_CONNTRACK_STATE_BIT(ctinfo); + if(sinfo->flags & IPT_CONNTRACK_STATE) { + if (ct) { + if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip != + ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip) + statebit |= IPT_CONNTRACK_STATE_SNAT; + + if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip != + ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip) + statebit |= IPT_CONNTRACK_STATE_DNAT; + } + + if (FWINV((statebit & sinfo->statemask) == 0, IPT_CONNTRACK_STATE)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_PROTO) { + if (!ct || FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, IPT_CONNTRACK_PROTO)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_ORIGSRC) { + if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip&sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, IPT_CONNTRACK_ORIGSRC)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_ORIGDST) { + if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip&sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, IPT_CONNTRACK_ORIGDST)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_REPLSRC) { + if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip&sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].src.ip, IPT_CONNTRACK_REPLSRC)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_REPLDST) { + if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip&sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, IPT_CONNTRACK_REPLDST)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_STATUS) { + if (!ct || FWINV((ct->status & sinfo->statusmask) == 0, IPT_CONNTRACK_STATUS)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_EXPIRES) { + unsigned long expires; + + if(!ct) + return 0; + + expires = timer_pending(&ct->timeout) ? (ct->timeout.expires - jiffies)/HZ : 0; + + if (FWINV(!(expires >= sinfo->expires_min && expires <= sinfo->expires_max), IPT_CONNTRACK_EXPIRES)) + return 0; + } + + return 1; +} + +static int check(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_conntrack_info))) + return 0; + + return 1; +} + +static struct ipt_match conntrack_match += { { NULL, NULL }, "conntrack", &match, &check, NULL, THIS_MODULE }; + +static int __init init(void) +{ + /* NULL if ip_conntrack not a module */ + if (ip_conntrack_module) + __MOD_INC_USE_COUNT(ip_conntrack_module); + return ipt_register_match(&conntrack_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&conntrack_match); + if (ip_conntrack_module) + __MOD_DEC_USE_COUNT(ip_conntrack_module); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_dscp.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_dscp.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_dscp.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_dscp.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,58 @@ +/* IP tables module for matching the value of the IPv4 DSCP field + * + * dscp.patch,v 1.1 2002/04/04 10:48:35 laforge Exp + * + * (C) 2002 by Harald Welte + * + * This software is distributed under the terms GNU GPL + */ + +#include +#include + +#include +#include + +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("IP tables DSCP 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_dscp_info *info = matchinfo; + const struct iphdr *iph = skb->nh.iph; + + u_int8_t sh_dscp = ((info->dscp << IPT_DSCP_SHIFT) & IPT_DSCP_MASK); + + return (iph->tos&IPT_DSCP_MASK == sh_dscp) ^ 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_dscp_info))) + return 0; + + return 1; +} + +static struct ipt_match dscp_match = { { NULL, NULL }, "dscp", &match, + &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&dscp_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&dscp_match); + +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_helper.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_helper.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_helper.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_helper.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,112 @@ +/* + * iptables module to match on related connections + * (c) 2001 Martin Josefsson + * + * Released under the terms of GNU GPLv2. + * + * 19 Mar 2002 Harald Welte +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_helper_info *info = matchinfo; + struct ip_conntrack_expect *exp; + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + if (!ct) { + DEBUGP("ipt_helper: Eek! invalid conntrack?\n"); + return 0; + } + + if (!ct->master) { + DEBUGP("ipt_helper: conntrack %p has no master\n", ct); + return 0; + } + + exp = ct->master; + if (!exp->expectant) { + DEBUGP("ipt_helper: expectation %p without expectant !?!\n", + exp); + return 0; + } + + if (!exp->expectant->helper) { + DEBUGP("ipt_helper: master ct %p has no helper\n", + exp->expectant); + return 0; + } + + DEBUGP("master's name = %s , info->name = %s\n", + exp->expectant->helper->name, info->name); + + return !strncmp(exp->expectant->helper->name, info->name, + strlen(exp->expectant->helper->name)) ^ info->invert; +} + +static int check(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_helper_info *info = matchinfo; + + info->name[29] = '\0'; + + /* verify size */ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_helper_info))) + return 0; + + /* verify that we actually should match anything */ + if ( strlen(info->name) == 0 ) + return 0; + + return 1; +} + +static struct ipt_match helper_match += { { NULL, NULL }, "helper", &match, &check, NULL, THIS_MODULE }; + +static int __init init(void) +{ + /* NULL if ip_conntrack not a module */ + if (ip_conntrack_module) + __MOD_INC_USE_COUNT(ip_conntrack_module); + return ipt_register_match(&helper_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&helper_match); + if (ip_conntrack_module) + __MOD_DEC_USE_COUNT(ip_conntrack_module); +} + +module_init(init); +module_exit(fini); + diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_ipv4options.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_ipv4options.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_ipv4options.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_ipv4options.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,170 @@ +/* + This is a module which is used to match ipv4 options. + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 11-mars-2001 Fabrice MARIE : initial development. + 12-july-2001 Fabrice MARIE : added router-alert otions matching. Fixed a bug with no-srr + 12-august-2001 Imran Patel : optimization of the match. + 18-november-2001 Fabrice MARIE : added [!] 'any' option match. +*/ + +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_ipv4options_info *info = matchinfo; /* match info for rule */ + const struct iphdr *iph = skb->nh.iph; + const struct ip_options *opt; + + if (iph->ihl * 4 == sizeof(struct iphdr)) { + /* No options, so we match only the "DONTs" and the "IGNOREs" */ + + if (((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT) || + ((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) || + ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR) || + ((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) || + ((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) || + ((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT)) + return 0; + return 1; + } + else { + if ((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT) + /* there are options, and we don't need to care which one */ + return 1; + else { + if ((info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) == IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) + /* there are options but we don't want any ! */ + return 0; + } + } + + opt = &(IPCB(skb)->opt); + + /* source routing */ + if ((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) { + if (!((opt->srr) & (opt->is_strictroute))) + return 0; + } + else if ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR) { + if (!((opt->srr) & (!opt->is_strictroute))) + return 0; + } + else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_SRR) == IPT_IPV4OPTION_DONT_MATCH_SRR) { + if (opt->srr) + return 0; + } + /* record route */ + if ((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) { + if (!opt->rr) + return 0; + } + else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_RR) == IPT_IPV4OPTION_DONT_MATCH_RR) { + if (opt->rr) + return 0; + } + /* timestamp */ + if ((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) { + if (!opt->ts) + return 0; + } + else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) == IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) { + if (opt->ts) + return 0; + } + /* router-alert option */ + if ((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT) { + if (!opt->router_alert) + return 0; + } + else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) { + if (opt->router_alert) + return 0; + } + + /* we match ! */ + return 1; +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + const struct ipt_ipv4options_info *info = matchinfo; /* match info for rule */ + /* Check the size */ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_ipv4options_info))) + return 0; + /* Now check the coherence of the data ... */ + if (((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT) && + (((info->options & IPT_IPV4OPTION_DONT_MATCH_SRR) == IPT_IPV4OPTION_DONT_MATCH_SRR) || + ((info->options & IPT_IPV4OPTION_DONT_MATCH_RR) == IPT_IPV4OPTION_DONT_MATCH_RR) || + ((info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) == IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) || + ((info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) || + ((info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) == IPT_IPV4OPTION_DONT_MATCH_ANY_OPT))) + return 0; /* opposites */ + if (((info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) == IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) && + (((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR) || + ((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) || + ((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) || + ((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) || + ((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT) || + ((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT))) + return 0; /* opposites */ + if (((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) && + ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR)) + return 0; /* cannot match in the same time loose and strict source routing */ + if ((((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) || + ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR)) && + ((info->options & IPT_IPV4OPTION_DONT_MATCH_SRR) == IPT_IPV4OPTION_DONT_MATCH_SRR)) + return 0; /* opposites */ + if (((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) && + ((info->options & IPT_IPV4OPTION_DONT_MATCH_RR) == IPT_IPV4OPTION_DONT_MATCH_RR)) + return 0; /* opposites */ + if (((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) && + ((info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) == IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP)) + return 0; /* opposites */ + if (((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT) && + ((info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT)) + return 0; /* opposites */ + + /* everything looks ok. */ + return 1; +} + +static struct ipt_match ipv4options_match += { { NULL, NULL }, "ipv4options", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + printk("ipt_ipv4options loading\n"); + return ipt_register_match(&ipv4options_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipv4options_match); + printk("ipt_ipv4options unloaded\n"); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_mark.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_mark.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_mark.c 2001-09-30 21:26:08.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_mark.c 2002-07-07 00:19:40.000000000 +0200 @@ -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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_mport.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_mport.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_mport.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_mport.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,112 @@ +/* Kernel module to match one of a list of TCP/UDP ports: ports are in + the same place so we can treat them as equal. */ +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +#if 0 +#define duprintf(format, args...) printk(format , ## args) +#else +#define duprintf(format, args...) +#endif + +/* Returns 1 if the port is matched by the test, 0 otherwise. */ +static inline int +ports_match(const struct ipt_mport *minfo, u_int16_t src, u_int16_t dst) +{ + unsigned int i; + unsigned int m; + u_int16_t pflags = minfo->pflags; + for (i=0, m=1; iports[i] == 65535) + return 0; + + s = minfo->ports[i]; + + if (pflags & m) { + e = minfo->ports[++i]; + m <<= 1; + } else + e = s; + + if (minfo->flags & IPT_MPORT_SOURCE + && src >= s && src <= e) + return 1; + + if (minfo->flags & IPT_MPORT_DESTINATION + && dst >= s && dst <= e) + return 1; + } + + return 0; +} + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct udphdr *udp = hdr; + const struct ipt_mport *minfo = matchinfo; + + /* Must be big enough to read ports. */ + if (offset == 0 && datalen < sizeof(struct udphdr)) { + /* We've been asked to examine this packet, and we + can't. Hence, no choice but to drop. */ + duprintf("ipt_mport:" + " Dropping evil offset=0 tinygram.\n"); + *hotdrop = 1; + return 0; + } + + /* Must not be a fragment. */ + return !offset + && ports_match(minfo, ntohs(udp->source), ntohs(udp->dest)); +} + +/* Called when user tries to insert an entry of this type. */ +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_mport))) + return 0; + + /* Must specify proto == TCP/UDP, no unknown flags or bad count */ + return (ip->proto == IPPROTO_TCP || ip->proto == IPPROTO_UDP) + && !(ip->flags & 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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_nth.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_nth.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_nth.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_nth.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,172 @@ +/* + This is a module which is used for match support for every Nth packet + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 2001-07-18 Fabrice MARIE : initial implementation. + 2001-09-20 Richard Wagner (rwagner@cloudnet.com) + * added support for multiple counters + * added support for matching on individual packets + in the counter cycle + +*/ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +/* + * State information. + */ +struct state { + spinlock_t lock; + u_int16_t number; +}; + +static struct state states[IPT_NTH_NUM_COUNTERS]; + +static int +ipt_nth_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + /* Parameters from userspace */ + const struct ipt_nth_info *info = matchinfo; + unsigned counter = info->counter; + if((counter < 0) || (counter >= IPT_NTH_NUM_COUNTERS)) + { + printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IPT_NTH_NUM_COUNTERS-1); + return 0; + }; + + spin_lock(&states[counter].lock); + + /* Are we matching every nth packet?*/ + if (info->packet == 0xFF) + { + /* We're matching every nth packet and only every nth packet*/ + /* Do we match or invert match? */ + if (info->not == 0) + { + if (states[counter].number == 0) + { + ++states[counter].number; + goto match; + } + if (states[counter].number >= info->every) + states[counter].number = 0; /* reset the counter */ + else + ++states[counter].number; + goto dontmatch; + } + else + { + if (states[counter].number == 0) + { + ++states[counter].number; + goto dontmatch; + } + if (states[counter].number >= info->every) + states[counter].number = 0; + else + ++states[counter].number; + goto match; + } + } + else + { + /* We're using the --packet, so there must be a rule for every value */ + if (states[counter].number == info->packet) + { + /* only increment the counter when a match happens */ + if (states[counter].number >= info->every) + states[counter].number = 0; /* reset the counter */ + else + ++states[counter].number; + goto match; + } + else + goto dontmatch; + } + + dontmatch: + /* don't match */ + spin_unlock(&states[counter].lock); + return 0; + + match: + spin_unlock(&states[counter].lock); + return 1; +} + +static int +ipt_nth_checkentry(const char *tablename, + const struct ipt_ip *e, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + /* Parameters from userspace */ + const struct ipt_nth_info *info = matchinfo; + unsigned counter = info->counter; + if((counter < 0) || (counter >= IPT_NTH_NUM_COUNTERS)) + { + printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IPT_NTH_NUM_COUNTERS-1); + return 0; + }; + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_nth_info))) { + printk("nth: matchsize %u != %u\n", matchsize, + IPT_ALIGN(sizeof(struct ipt_nth_info))); + return 0; + } + + states[counter].number = info->startat; + + return 1; +} + +static struct ipt_match ipt_nth_reg = { + {NULL, NULL}, + "nth", + ipt_nth_match, + ipt_nth_checkentry, + NULL, + THIS_MODULE }; + +static int __init init(void) +{ + unsigned counter; + memset(&states, 0, sizeof(states)); + if (ipt_register_match(&ipt_nth_reg)) + return -EINVAL; + + for(counter = 0; counter < IPT_NTH_NUM_COUNTERS; counter++) + { + spin_lock_init(&(states[counter].lock)); + }; + + printk("ipt_nth match loaded\n"); + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipt_nth_reg); + printk("ipt_nth match unloaded\n"); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_owner.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_owner.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_owner.c 2001-09-30 21:26:08.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_owner.c 2002-07-07 00:19:40.000000000 +0200 @@ -12,6 +12,38 @@ #include 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; @@ -115,6 +147,12 @@ return 0; } + if(info->match & IPT_OWNER_COMM) { + if (!match_comm(skb, info->comm) ^ + !!(info->invert & IPT_OWNER_COMM)) + return 0; + } + return 1; } diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_pkttype.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_pkttype.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_pkttype.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_pkttype.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +static int match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_pkttype_info *info = matchinfo; + + return (skb->pkt_type == info->pkttype) ^ info->invert; +} + +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_LOCAL_IN) + | (1 << NF_IP_FORWARD))) { + printk("ipt_pkttype: only valid for PRE_ROUTING, LOCAL_IN or FORWARD.\n"); + return 0; + } +*/ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_pkttype_info))) + return 0; + + return 1; +} + +static struct ipt_match pkttype_match += { { NULL, NULL }, "pkttype", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&pkttype_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&pkttype_match); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_pool.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_pool.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_pool.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_pool.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,71 @@ +/* Kernel module to match an IP address pool. */ + +#include +#include +#include + +#include +#include +#include + +static inline int match_pool( + ip_pool_t index, + __u32 addr, + int inv +) { + if (ip_pool_match(index, ntohl(addr))) + inv = !inv; + return inv; +} + +static int match( + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop +) { + const struct ipt_pool_info *info = matchinfo; + const struct iphdr *iph = skb->nh.iph; + + if (info->src != IP_POOL_NONE && !match_pool(info->src, iph->saddr, + info->flags&IPT_POOL_INV_SRC)) + return 0; + + if (info->dst != IP_POOL_NONE && !match_pool(info->dst, iph->daddr, + info->flags&IPT_POOL_INV_DST)) + return 0; + + return 1; +} + +static int checkentry( + const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask +) { + if (matchsize != IPT_ALIGN(sizeof(struct ipt_pool_info))) + return 0; + return 1; +} + +static struct ipt_match pool_match += { { NULL, NULL }, "pool", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&pool_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&pool_match); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_psd.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_psd.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_psd.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_psd.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,360 @@ +/* + This is a module which is used for PSD (portscan detection) + Derived from scanlogd v2.1 written by Solar Designer + and LOG target module. + + Copyright (C) 2000,2001 astaro AG + + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 2000-05-04 Markus Hennig : initial + 2000-08-18 Dennis Koslowski : first release + 2000-12-01 Dennis Koslowski : UDP scans detection added + 2001-01-02 Dennis Koslowski : output modified + 2001-02-04 Jan Rekorajski : converted from target to match +*/ + +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +MODULE_LICENSE("GPL"); + +#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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_quota.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_quota.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_quota.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_quota.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,81 @@ +/* + * netfilter module to enforce network quotas + * + * Sam Johnston + */ +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +static spinlock_t quota_lock = SPIN_LOCK_UNLOCKED; + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, const void *hdr, u_int16_t datalen, int *hotdrop) +{ + + struct ipt_quota_info *q = (struct ipt_quota_info *) matchinfo; + + spin_lock_bh("a_lock); + + if (q->quota >= datalen) { + /* we can afford this one */ + q->quota -= datalen; + spin_unlock_bh("a_lock); + +#ifdef DEBUG_IPT_QUOTA + printk("IPT Quota OK: %llu datlen %d \n", q->quota, datalen); +#endif + return 1; + } + + /* so we do not allow even small packets from now on */ + q->quota = 0; + +#ifdef DEBUG_IPT_QUOTA + printk("IPT Quota Failed: %llu datlen %d \n", q->quota, datalen); +#endif + + spin_unlock_bh("a_lock); + return 0; +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, unsigned int matchsize, unsigned int hook_mask) +{ + /* TODO: spinlocks? sanity checks? */ + if (matchsize != IPT_ALIGN(sizeof (struct ipt_quota_info))) + return 0; + + return 1; +} + +static struct ipt_match quota_match + = { {NULL, NULL}, "quota", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init +init(void) +{ + return ipt_register_match("a_match); +} + +static void __exit +fini(void) +{ + ipt_unregister_match("a_match); +} + +module_init(init); +module_exit(fini); + diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_random.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_random.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_random.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_random.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,96 @@ +/* + This is a module which is used for a "random" match support. + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 2001-10-14 Fabrice MARIE : initial implementation. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +static int +ipt_rand_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + /* Parameters from userspace */ + const struct ipt_rand_info *info = matchinfo; + u_int8_t random_number; + + /* get 1 random number from the kernel random number generation routine */ + get_random_bytes((void *)(&random_number), 1); + + /* Do we match ? */ + if (random_number <= info->average) + return 1; + else + return 0; +} + +static int +ipt_rand_checkentry(const char *tablename, + const struct ipt_ip *e, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + /* Parameters from userspace */ + const struct ipt_rand_info *info = matchinfo; + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_rand_info))) { + printk("ipt_random: matchsize %u != %u\n", matchsize, + IPT_ALIGN(sizeof(struct ipt_rand_info))); + return 0; + } + + /* must be 1 <= average % <= 99 */ + /* 1 x 2.55 = 2 */ + /* 99 x 2.55 = 252 */ + if ((info->average < 2) || (info->average > 252)) { + printk("ipt_random: invalid average %u\n", info->average); + return 0; + } + + return 1; +} + +static struct ipt_match ipt_rand_reg = { + {NULL, NULL}, + "random", + ipt_rand_match, + ipt_rand_checkentry, + NULL, + THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_match(&ipt_rand_reg)) + return -EINVAL; + + printk("ipt_random match loaded\n"); + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipt_rand_reg); + printk("ipt_random match unloaded\n"); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_realm.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_realm.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_realm.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_realm.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,66 @@ +/* Kernel module to match realm from routing. */ +#include +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + struct dst_entry *dst; + u32 id; + const struct ipt_realm_info *info = matchinfo; + + if((dst = skb->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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_recent.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_recent.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_recent.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_recent.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,518 @@ +/* Kernel module to check if the source address has been seen recently. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Defaults, these can be overridden on the module command-line. */ +static int ip_list_tot = 40; +static int ip_pkt_list_tot = 10; + +MODULE_AUTHOR("Stephen Frost "); +MODULE_DESCRIPTION("IP tables recently seen matching module"); +MODULE_LICENSE("GPL"); +MODULE_PARM(ip_list_tot,"i"); +MODULE_PARM(ip_pkt_list_tot,"i"); +MODULE_PARM_DESC(ip_list_tot,"number of IPs to remember per list"); +MODULE_PARM_DESC(ip_pkt_list_tot,"number of packets per IP to remember"); + +/* Structure of our list of recently seen addresses. */ +struct recent_ip_list { + u_int32_t saddr; + u_int8_t ttl; + u_int32_t last_seen; + u_int32_t *last_pkts; + u_int32_t oldest_pkt; +}; + +/* Structure of our linked list of tables of recent lists. */ +struct recent_ip_tables { + char name[200]; + int count; + struct recent_ip_list *table; + struct recent_ip_tables *next; + spinlock_t list_lock; + struct proc_dir_entry *status_proc; +}; + +/* Our current list of addresses we have recently seen. + * Only added to on a --set, and only updated on --set || --update + */ +static struct recent_ip_tables *r_tables; + +/* We protect r_list with this spinlock so two processors are not modifying + * the list at the same time. + */ +static spinlock_t recent_lock = SPIN_LOCK_UNLOCKED; + +/* Our /proc/net/ipt_recent entry */ +static struct proc_dir_entry *proc_net_ipt_recent; + +/* Function declaration for later. */ +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop); + +/* This is the function which produces the output for our /proc output + * interface which lists each IP address, the last seen time and the + * other recent times the address was seen. + */ + +static int ip_recent_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data) +{ + int len = 0, count, last_len = 0, pkt_count; + off_t pos = 0; + off_t begin = 0; + struct recent_ip_tables *curr_table; + + curr_table = (struct recent_ip_tables*) data; + + spin_lock_bh(&curr_table->list_lock); + for(count = 0; count < ip_list_tot; count++) { + if(!curr_table->table[count].saddr) continue; + last_len = len; + len += sprintf(buffer+len,"src=%u.%u.%u.%u ",NIPQUAD(curr_table->table[count].saddr)); + len += sprintf(buffer+len,"ttl: %d ",curr_table->table[count].ttl); + len += sprintf(buffer+len,"last_seen: %d ",curr_table->table[count].last_seen); + len += sprintf(buffer+len,"oldest_pkt: %d ",curr_table->table[count].oldest_pkt); + len += sprintf(buffer+len,"last_pkts: %d",curr_table->table[count].last_pkts[0]); + for(pkt_count = 1; pkt_count < ip_pkt_list_tot; pkt_count++) { + if(!curr_table->table[count].last_pkts[pkt_count]) break; + len += sprintf(buffer+len,", %d",curr_table->table[count].last_pkts[pkt_count]); + } + len += sprintf(buffer+len,"\n"); + pos = begin + len; + if(pos < offset) { len = 0; begin = pos; } + if(pos > offset + length) { len = last_len; break; } + } + + *start = buffer + (offset - begin); + len -= (offset - begin); + if(len > length) len = length; + + spin_unlock_bh(&curr_table->list_lock); + return len; +} + +/* ip_recent_ctrl provides an interface for users to modify the table + * directly. This allows adding entries, removing entries, and + * flushing the entire table. + * This is done by opening up the appropriate table for writing and + * sending one of: + * xx.xx.xx.xx -- Add entry to table with current time + * +xx.xx.xx.xx -- Add entry to table with current time + * -xx.xx.xx.xx -- Remove entry from table + * -0.0.0.0 -- Flush table, remove all entries + */ + +static int ip_recent_ctrl(struct file *file, const char *input, unsigned long size, void *data) +{ + static const u_int32_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff }; + u_int32_t val; + int base; + char c, *cp; + union iaddr { + uint8_t bytes[4]; + uint32_t word; + } res; + uint8_t *pp = res.bytes; + int digit; + + char buffer[1024]; + int len, check_set = 0, count; + u_int32_t saddr = 0; + struct sk_buff skb; + struct ipt_recent_info info; + struct recent_ip_tables *curr_table; + + curr_table = (struct recent_ip_tables*) data; + + if(size > 1024) len = 1024; else len = size; + + if(copy_from_user(buffer,input,len)) return -EFAULT; + + buffer[len-1] = '\0'; + + cp = buffer; + if(buffer[0] == '+') { check_set = IPT_RECENT_SET; cp++; } + if(buffer[0] == '-') { check_set = IPT_RECENT_REMOVE; cp++; } + if(buffer[0] >= '0' && buffer[0] <= '9') check_set = IPT_RECENT_SET; + if(!check_set) return len; + + /* Get saddr (effectively inet_aton()) */ + /* Shamelessly stolen from libc, a function in the kernel for doing + * this would, of course, be greatly preferred, but our options appear + * to be rather limited, so we will just do it ourselves here. + */ + res.word = 0; + + c = *cp; + for(;;) { + if(!isdigit(c)) return len; + val = 0; base = 10; digit = 0; + if(c == '0') { + c = *++cp; + if(c == 'x' || c == 'X') base = 16, c = *++cp; + else { base = 8; digit = 1; } + } + for(;;) { + if(isascii(c) && isdigit(c)) { + if(base == 8 && (c == '8' || c == '0')) return len; + val = (val * base) + (c - '0'); + c = *++cp; + digit = 1; + } else if(base == 16 && isascii(c) && isxdigit(c)) { + val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + digit = 1; + } else break; + } + if(c == '.') { + if(pp > res.bytes + 2 || val > 0xff) return len; + *pp++ = val; + c = *++cp; + } else break; + } + if(c != '\0' && (!isascii(c) || !isspace(c))) return len; + if(!digit) return len; + + if(val > max[pp - res.bytes]) return len; + saddr = res.word | htonl(val); + + if(!saddr && check_set == IPT_RECENT_SET) return len; + + /* Check if we are asked to flush the entire table */ + if(!saddr && check_set == IPT_RECENT_REMOVE) { + spin_lock_bh(&curr_table->list_lock); + for(count = 0; count < ip_list_tot; count++) { + curr_table->table[count].last_seen = 0; + curr_table->table[count].saddr = 0; + curr_table->table[count].ttl = 0; + memset(curr_table->table[count].last_pkts,0,ip_pkt_list_tot*sizeof(u_int32_t)); + curr_table->table[count].oldest_pkt = 0; + } + spin_unlock_bh(&curr_table->list_lock); + return len; + } + + /* Set up and just call match */ + info.seconds = 0; + info.hit_count = 0; + info.check_set = check_set; + info.invert = 0; + strncpy(info.name,curr_table->name,200); + + skb.nh.iph = kmalloc(sizeof(struct iphdr),GFP_KERNEL); + if(!skb.nh.iph) { return -ENOMEM; } + + skb.nh.iph->saddr = saddr; + match(&skb,NULL,NULL,&info,0,NULL,sizeof(info),NULL); + kfree(skb.nh.iph); + + return len; +} + +/* 'match' is our primary function, called by the kernel whenever a rule is + * hit with our module as an option to it. + * What this function does depends on what was specifically asked of it by + * the user: + * --set -- Add or update last seen time of the source address of the packet + * -- matchinfo->check_set == IPT_RECENT_SET + * --rcheck -- Just check if the source address is in the list + * -- matchinfo->check_set == IPT_RECENT_CHECK + * --update -- If the source address is in the list, update last_seen + * -- matchinfo->check_set == IPT_RECENT_UPDATE + * --remove -- If the source address is in the list, remove it + * -- matchinfo->check_set == IPT_RECENT_REMOVE + * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds + * -- matchinfo->seconds + * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times + * -- matchinfo->hit_count + * --seconds and --hitcount can be combined + */ +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + int count, pkt_count, oldest = 0, hits_found, ans; + unsigned long now = jiffies; + unsigned long oldest_time; + const struct ipt_recent_info *info = matchinfo; + u_int32_t saddr = skb->nh.iph->saddr; + u_int8_t ttl = skb->nh.iph->ttl; + struct recent_ip_tables *curr_table; + struct recent_ip_tables *last_table; + struct recent_ip_list *r_list; + + /* Default is false ^ info->invert */ + ans = info->invert; + + /* Find the right table */ + spin_lock_bh(&recent_lock); + curr_table = r_tables; + while( (last_table = curr_table) && strncmp(info->name,curr_table->name,200) && (curr_table = curr_table->next) ); + spin_unlock_bh(&recent_lock); + + /* Table with this name not found, match impossible */ + if(!curr_table) { return ans; } + + /* Make sure no one is changing the list while we work with it */ + spin_lock_bh(&curr_table->list_lock); + + r_list = curr_table->table; + + for(count = 0, oldest_time = r_list[0].last_seen; count < ip_list_tot; count++) { + /* Keep track of empty spaces which can be filled up later */ + if(!r_list[count].saddr) { oldest = count; continue; } + /* Keep track of the oldest entry if we have been asked to add this entry + * to the table if it does not exist. */ + if(info->check_set & IPT_RECENT_SET && r_list[count].last_seen < oldest_time) { + oldest_time = r_list[count].last_seen; + oldest = count; + } + if(r_list[count].saddr == saddr) { + /* We have a match on address, now to make sure it meets all requirements for a + * full match. */ + /* Check for TTL match if requested. If TTL is zero then a match would never + * happen, so match regardless of existing TTL in that case. Zero means the + * entry was added via the /proc interface anyway, so we will just use the + * first TTL we get for that IP address. */ + if(info->check_set & IPT_RECENT_TTL && r_list[count].ttl && r_list[count].ttl != ttl) continue; + if(info->check_set & IPT_RECENT_CHECK || info->check_set & IPT_RECENT_UPDATE) { + if(!info->seconds && !info->hit_count) ans = !info->invert; else ans = info->invert; + if(info->seconds && !info->hit_count) { + if(time_before(now,r_list[count].last_seen+info->seconds*HZ)) ans = !info->invert; else ans = info->invert; + } + if(info->seconds && info->hit_count) { + for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) { + if(time_before(now,r_list[count].last_pkts[pkt_count]+info->seconds*HZ)) hits_found++; + } + if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert; + } + if(info->hit_count && !info->seconds) { + for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) { + if(r_list[count].last_pkts[pkt_count] == 0) break; + hits_found++; + } + if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert; + } + } + /* If and only if we have been asked to SET, or to UPDATE (on match) do we add the + * current timestamp to the last_seen. */ + if((info->check_set & IPT_RECENT_SET && (ans = !info->invert)) || (info->check_set & IPT_RECENT_UPDATE && ans)) { + r_list[count].last_seen = now; + /* If asked to match on TTL we will not hit here unless the TTL already matches, + * or is zero. If zero then the IP address was added via /proc, so we need to + * add the TTL here for the IP address. If non-zero, then we are not matching + * on TTL, or the TTL already matches, so it does not hurt to set it here. */ + r_list[count].ttl = ttl; + r_list[count].last_pkts[r_list[count].oldest_pkt] = now; + r_list[count].oldest_pkt = ++r_list[count].oldest_pkt % ip_pkt_list_tot; + } + /* If we have been asked to remove the entry from the list, just set it to 0 */ + if(info->check_set & IPT_RECENT_REMOVE) { + r_list[count].last_seen = 0; + r_list[count].saddr = 0; + r_list[count].ttl = 0; + memset(r_list[count].last_pkts,0,ip_pkt_list_tot*sizeof(u_int32_t)); + r_list[count].oldest_pkt = 0; + ans = !info->invert; + } + spin_unlock_bh(&curr_table->list_lock); + return ans; + } + } + + /* If the address is not currently in the list and we have been asked to SET + * then we need to add it to the list. We do this by replacing the oldest + * entry in the list which is determined when we step through the list. */ + if(info->check_set & IPT_RECENT_SET) { + memset(r_list[oldest].last_pkts,0,ip_pkt_list_tot*sizeof(u_int32_t)); + r_list[oldest].saddr = saddr; + r_list[oldest].ttl = ttl; + r_list[oldest].last_seen = now; + r_list[oldest].last_pkts[0] = now; + r_list[oldest].oldest_pkt = 1; + + ans = !info->invert; + } + + spin_unlock_bh(&curr_table->list_lock); + return ans; +} + +/* This function is to verify that the rule given during the userspace iptables + * command is correct. + * If the command is valid then we check if the table name referred to by the + * rule exists, if not it is created. + */ +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + int flag = 0, c; + u_int32_t *hold; + const struct ipt_recent_info *info = matchinfo; + struct recent_ip_tables *curr_table, *last_table; + if (matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return 0; + + /* seconds and hit_count only valid for CHECK/UPDATE */ + if(info->check_set & IPT_RECENT_SET) { flag++; if(info->seconds || info->hit_count) return 0; } + if(info->check_set & IPT_RECENT_REMOVE) { flag++; if(info->seconds || info->hit_count) return 0; } + if(info->check_set & IPT_RECENT_CHECK) flag++; + if(info->check_set & IPT_RECENT_UPDATE) flag++; + + /* One and only one of these should ever be set */ + if(flag != 1) return 0; + + /* Name must be set to something */ + if(!info->name || !info->name[0]) return 0; + + /* Things look good, create a list for this if it does not exist */ + /* Lock the linked list while we play with it */ + spin_lock_bh(&recent_lock); + + /* Look for an entry with this name already created */ + /* Finds the end of the list and the entry before the end if current name does not exist */ + curr_table = r_tables; + while( (last_table = curr_table) && strncmp(info->name,curr_table->name,200) && (curr_table = curr_table->next) ); + + /* If a table already exists just increment the count on that table and return */ + if(curr_table) { curr_table->count++; spin_unlock_bh(&recent_lock); return 1; } + + /* Table with this name not found */ + /* Allocate memory for new linked list item */ + curr_table = kmalloc(sizeof(struct recent_ip_tables),GFP_KERNEL); + if(curr_table == NULL) { spin_unlock_bh(&recent_lock); return -ENOMEM; } + + curr_table->list_lock = SPIN_LOCK_UNLOCKED; + curr_table->next = NULL; + curr_table->count = 1; + strncpy(curr_table->name,info->name,200); + + /* Allocate memory for this table and the list of packets in each entry. */ + curr_table->table = kmalloc(sizeof(struct recent_ip_list)*ip_list_tot,GFP_KERNEL); + if(curr_table->table == NULL) { spin_unlock_bh(&recent_lock); return -ENOMEM; } + memset(curr_table->table,0,sizeof(struct recent_ip_list)*ip_list_tot); + hold = kmalloc(sizeof(u_int32_t)*ip_pkt_list_tot*ip_list_tot,GFP_KERNEL); + if(hold == NULL) { kfree(curr_table->table); spin_unlock_bh(&recent_lock); return -ENOMEM; } + for(c = 0; c < ip_list_tot; c++) { + curr_table->table[c].last_pkts = hold + c*ip_pkt_list_tot; + } + + /* Create our proc 'status' entry. */ + curr_table->status_proc = create_proc_entry(curr_table->name, 0644, proc_net_ipt_recent); + if (curr_table->status_proc) curr_table->status_proc->owner = THIS_MODULE; + else { + kfree(curr_table->table); + kfree(curr_table); + kfree(hold); + return -ENOMEM; + } + curr_table->status_proc->read_proc = ip_recent_get_info; + curr_table->status_proc->write_proc = ip_recent_ctrl; + curr_table->status_proc->data = curr_table; + + /* Put the new table in place */ + if(!last_table) r_tables = curr_table; else last_table->next = curr_table; + + spin_unlock_bh(&recent_lock); + + return 1; +} + +/* This function is called in the event that a rule matching this module is + * removed. + * When this happens we need to check if there are no other rules matching + * the table given. If that is the case then we remove the table and clean + * up its memory. + */ +static void +destroy(void *matchinfo, unsigned int matchsize) +{ + const struct ipt_recent_info *info = matchinfo; + struct recent_ip_tables *curr_table, *last_table; + if(matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return; + + /* Lock the linked list while we play with it */ + spin_lock_bh(&recent_lock); + + /* Look for an entry with this name already created */ + /* Finds the end of the list and the entry before the end if current name does not exist */ + last_table = NULL; + curr_table = r_tables; + if(!curr_table) { spin_unlock_bh(&recent_lock); return; } + while( strncmp(info->name,curr_table->name,200) && (last_table = curr_table) && (curr_table = curr_table->next) ); + + /* If a table does not exist then do nothing and return */ + if(!curr_table) { spin_unlock_bh(&recent_lock); return; } + + curr_table->count--; + /* If count is still non-zero then there are still rules referenceing it so we do nothing */ + if(curr_table->count) { spin_unlock_bh(&recent_lock); return; } + + /* Count must be zero so we remove this table from the list */ + if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next; + + /* lock to make sure any late-runners still using this after we removed it from + * the list finish up then remove everything */ + spin_lock_bh(&curr_table->list_lock); + remove_proc_entry(curr_table->name,proc_net_ipt_recent); + kfree(curr_table->table[0].last_pkts); + kfree(curr_table->table); + spin_unlock_bh(&curr_table->list_lock); + kfree(curr_table); + + spin_unlock_bh(&recent_lock); + + return; +} + +/* This is the structure we pass to ipt_register to register our + * module with iptables. + */ +static struct ipt_match recent_match += { { NULL, NULL }, "recent", &match, &checkentry, &destroy, THIS_MODULE }; + +/* Kernel module initialization. */ +static int __init init(void) +{ + proc_net_ipt_recent = proc_mkdir("ipt_recent",proc_net); + if(!proc_net_ipt_recent) return -ENOMEM; + + return ipt_register_match(&recent_match); +} + +/* Kernel module destruction. */ +static void __exit fini(void) +{ + ipt_unregister_match(&recent_match); + + remove_proc_entry("ipt_recent",proc_net); +} + +/* Register our module with the kernel. */ +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_record_rpc.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_record_rpc.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_record_rpc.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_record_rpc.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,169 @@ +/* Kernel module to match RPC connection tracking information. */ +/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EXPORT_NO_SYMBOLS; + +extern struct list_head expect_rpc_list_tcp; +extern struct module *ip_conntrack_rpc_tcp; +extern struct list_head expect_rpc_list_udp; +extern struct module *ip_conntrack_rpc_udp; +DECLARE_RWLOCK_EXTERN(ipct_rpc_tcp_lock); +DECLARE_RWLOCK_EXTERN(ipct_rpc_udp_lock); + +#define ASSERT_READ_LOCK(x) \ +do { \ + if (x == &expect_rpc_list_udp) \ + MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock); \ + else if (x == &expect_rpc_list_tcp) \ + MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock); \ +} while (0) + +#define ASSERT_WRITE_LOCK(x) \ +do { \ + if (x == &expect_rpc_list_udp) \ + MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock); \ + else if (x == &expect_rpc_list_tcp) \ + MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock); \ +} while (0) +#include + + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + struct expect_rpc *rpc_bind; + const u_int32_t *data; + enum ip_conntrack_dir dir; + const struct tcphdr *tcp; + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + if (ct) { + dir = CTINFO2DIR(ctinfo); + + /* This does sanity check in UDP or TCP packets like their */ + /* respective modules and permits only get port procedure in */ + /* client communications with portmapper */ + switch(ct->tuplehash[0].tuple.dst.protonum){ + + case IPPROTO_UDP: + if(offset == 0 && datalen < sizeof(struct udphdr)) { + *hotdrop = 1; + return 0; + } + if(ntohs(ct->tuplehash[dir].tuple.dst.u.all) == 111){ + + if((datalen - sizeof(struct udphdr)) != 56) + return 0; + + data = (const u_int32_t *)hdr + 7; + if(IXDR_GET_INT32(data) != 3) + return 0; + else + return (1 && (!offset)); + } + if(ntohs(ct->tuplehash[!dir].tuple.dst.u.all) == 111) + return (1 && (!offset)); + + case IPPROTO_TCP: + if(offset == 0 && datalen < sizeof(struct tcphdr)) { + *hotdrop = 1; + return 0; + } + if(ntohs(ct->tuplehash[dir].tuple.dst.u.all) == 111) { + tcp = hdr; + if(datalen == (tcp->doff * 4)) return (1 && (!offset)); + + /* Tests if packet len is ok */ + if((datalen - (tcp->doff * 4)) != 60) + return 0; + data = (const u_int32_t *)tcp + tcp->doff + 6; + + if(IXDR_GET_INT32(data) != 3) + return 0; + else + return (1 && (!offset)); + } + if(ntohs(ct->tuplehash[!dir].tuple.dst.u.all) == 111) + return (1 && (!offset)); + } + + /* Client using UDP in communication with portmapper */ + WRITE_LOCK(&ipct_rpc_udp_lock); + rpc_bind = LIST_FIND(&expect_rpc_list_udp, expect_rpc_cmp, struct expect_rpc *, + ntohs(ct->tuplehash[0].tuple.dst.u.all), + ct->tuplehash[1].tuple.src.ip, ct->tuplehash[0].tuple.src.ip, + ct->tuplehash[0].tuple.dst.protonum); + + if(rpc_bind) { + if(del_timer(&rpc_bind->timeout)) { + rpc_bind->timeout.expires = jiffies + EXPIRES; + add_timer(&rpc_bind->timeout); + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + /* Must not be a fragment */ + return (1 && (!offset)); + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + + /* Client using TCP in communication with portmapper */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + rpc_bind = LIST_FIND(&expect_rpc_list_tcp, expect_rpc_cmp, struct expect_rpc *, + ntohs(ct->tuplehash[0].tuple.dst.u.all), + ct->tuplehash[1].tuple.src.ip, ct->tuplehash[0].tuple.src.ip, + ct->tuplehash[0].tuple.dst.protonum); + + if (rpc_bind) { + if (del_timer(&rpc_bind->timeout)) { + rpc_bind->timeout.expires = jiffies + EXPIRES; + add_timer(&rpc_bind->timeout); + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return (1 && (!offset)); + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return 0; + } + return 0; +} + + +static struct ipt_match record_rpc_match += { { NULL, NULL }, "record_rpc", &match, NULL, NULL, THIS_MODULE }; + +int __init init(void) +{ + __MOD_INC_USE_COUNT(ip_conntrack_module); + __MOD_INC_USE_COUNT(ip_conntrack_rpc_udp); + __MOD_INC_USE_COUNT(ip_conntrack_rpc_tcp); + return ipt_register_match(&record_rpc_match); +} + +void __exit cleanup(void) +{ + ipt_unregister_match(&record_rpc_match); + __MOD_DEC_USE_COUNT(ip_conntrack_module); + __MOD_DEC_USE_COUNT(ip_conntrack_rpc_tcp); + __MOD_DEC_USE_COUNT(ip_conntrack_rpc_udp); +} + +module_init(init); +module_exit(cleanup); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_string.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_string.c --- linux-2.4.18_vanilla/net/ipv4/netfilter/ipt_string.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv4/netfilter/ipt_string.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,214 @@ +/* Kernel module to match a string into a packet. + * + * Copyright (C) 2000 Emmanuel Roger + * + * ChangeLog + * 19.02.2002: Gianni Tedesco + * Fixed SMP re-entrancy problem using per-cpu data areas + * for the skip/shift tables. + * 02.05.2001: Gianni Tedesco + * Fixed kernel panic, due to overrunning boyer moore string + * tables. Also slightly tweaked heuristic for deciding what + * search algo to use. + * 27.01.2001: Gianni Tedesco + * Implemented Boyer Moore Sublinear search algorithm + * alongside the existing linear search based on memcmp(). + * Also a quick check to decide which method to use on a per + * packet basis. + */ + +#include +#include +#include +#include +#include + +#include +#include + +struct string_per_cpu { + int *skip; + int *shift; + int *len; +}; + +struct string_per_cpu *bm_string_data=NULL; + +/* Boyer Moore Sublinear string search - VERY FAST */ +char *search_sublinear (char *needle, char *haystack, int needle_len, int haystack_len) +{ + int M1, right_end, sk, sh; + int ended, j, i; + + int *skip, *shift, *len; + + /* use data suitable for this CPU */ + shift=bm_string_data[smp_processor_id()].shift; + skip=bm_string_data[smp_processor_id()].skip; + len=bm_string_data[smp_processor_id()].len; + + /* Setup skip/shift tables */ + M1 = right_end = needle_len-1; + for (i = 0; i < BM_MAX_HLEN; i++) skip[i] = needle_len; + for (i = 0; needle[i]; i++) skip[needle[i]] = M1 - i; + + for (i = 1; i < needle_len; i++) { + for (j = 0; j < needle_len && needle[M1 - j] == needle[M1 - i - j]; j++); + len[i] = j; + } + + shift[0] = 1; + for (i = 1; i < needle_len; i++) shift[i] = needle_len; + for (i = M1; i > 0; i--) shift[len[i]] = i; + ended = 0; + + for (i = 0; i < needle_len; i++) { + if (len[i] == M1 - i) ended = i; + if (ended) shift[i] = ended; + } + + /* Do the search*/ + while (right_end < haystack_len) + { + for (i = 0; i < needle_len && haystack[right_end - i] == needle[M1 - i]; i++); + if (i == needle_len) { + return haystack+(right_end - M1); + } + + sk = skip[haystack[right_end - i]]; + sh = shift[i]; + right_end = max(right_end - i + sk, right_end + sh); + } + + return NULL; +} + +/* Linear string search based on memcmp() */ +char *search_linear (char *needle, char *haystack, int needle_len, int haystack_len) +{ + char *k = haystack + (haystack_len-needle_len); + char *t = haystack; + + while ( t++ < k ) { + if ( memcmp(t, needle, needle_len) == 0 ) return t; + } + + return NULL; +} + + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_string_info *info = matchinfo; + struct iphdr *ip = skb->nh.iph; + int hlen, nlen; + char *needle, *haystack; + proc_ipt_search search=search_linear; + + if ( !ip ) return 0; + + /* get lenghts, and validate them */ + nlen=info->len; + hlen=ntohs(ip->tot_len)-(ip->ihl*4); + if ( nlen > hlen ) return 0; + + needle=(char *)&info->string; + haystack=(char *)ip+(ip->ihl*4); + + /* The sublinear search comes in to its own + * on the larger packets */ + if ( (hlen>IPT_STRING_HAYSTACK_THRESH) && + (nlen>IPT_STRING_NEEDLE_THRESH) ) { + if ( hlen < BM_MAX_HLEN ) { + search=search_sublinear; + }else{ + if (net_ratelimit()) + printk(KERN_INFO "ipt_string: Packet too big " + "to attempt sublinear string search " + "(%d bytes)\n", hlen ); + } + } + + return ((search(needle, haystack, nlen, hlen)!=NULL) ^ info->invert); +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_string_info))) + return 0; + + return 1; +} + +void string_freeup_data(void) +{ + int c; + + if ( bm_string_data ) { + for(c=0; c : initial development. + 2001-21-05 Fabrice MARIE : bug fix in the match code, + thanks to "Zeng Yu" for bug report. + 2001-26-09 Fabrice MARIE : force the match to be in LOCAL_IN or PRE_ROUTING only. + 2001-30-11 Fabrice : added the possibility to use the match in FORWARD/OUTPUT with a little hack, + added Nguyen Dang Phuoc Dong patch to support timezones. +*/ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Fabrice MARIE "); +MODULE_DESCRIPTION("Match arrival timestamp"); +MODULE_LICENSE("GPL"); + +struct tm +{ + int tm_sec; /* Seconds. [0-60] (1 leap second) */ + int tm_min; /* Minutes. [0-59] */ + int tm_hour; /* Hours. [0-23] */ + int tm_mday; /* Day. [1-31] */ + int tm_mon; /* Month. [0-11] */ + int tm_year; /* Year - 1900. */ + int tm_wday; /* Day of week. [0-6] */ + int tm_yday; /* Days in year.[0-365] */ + int tm_isdst; /* DST. [-1/0/1]*/ + + long int tm_gmtoff; /* we don't care, we count from GMT */ + const char *tm_zone; /* we don't care, we count from GMT */ +}; + +void +localtime(const time_t *timepr, struct tm *r); + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_time_info *info = matchinfo; /* match info for rule */ + struct tm currenttime; /* time human readable */ + u_int8_t days_of_week[7] = {64, 32, 16, 8, 4, 2, 1}; + u_int16_t packet_time; + struct timeval kerneltimeval; + time_t packet_local_time; + + /* if kerneltime=1, we don't read the skb->timestamp but kernel time instead */ + if (info->kerneltime) + { + do_gettimeofday(&kerneltimeval); + packet_local_time = kerneltimeval.tv_sec; + } + else + packet_local_time = skb->stamp.tv_sec; + + /* Transform the timestamp of the packet, in a human readable form */ + localtime(&packet_local_time, ¤ttime); + + /* check if we match this timestamp, we start by the days... */ + if ((days_of_week[currenttime.tm_wday] & info->days_match) != days_of_week[currenttime.tm_wday]) + return 0; /* the day doesn't match */ + + /* ... check the time now */ + packet_time = (currenttime.tm_hour * 60) + currenttime.tm_min; + if ((packet_time < info->time_start) || (packet_time > info->time_stop)) + return 0; + + /* here we match ! */ + return 1; +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_time_info *info = matchinfo; /* match info for rule */ + + /* First, check that we are in the correct hook */ + /* PRE_ROUTING, LOCAL_IN or FROWARD */ + if (hook_mask + & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))) + { + printk("ipt_time: error, only valid for PRE_ROUTING, LOCAL_IN, FORWARD and OUTPUT)\n"); + return 0; + } + /* we use the kerneltime if we are in forward or output */ + info->kerneltime = 1; + if (hook_mask & ~((1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))) + /* if not, we use the skb time */ + info->kerneltime = 0; + + /* Check the size */ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_time_info))) + return 0; + /* Now check the coherence of the data ... */ + if ((info->time_start > 1439) || /* 23*60+59 = 1439*/ + (info->time_stop > 1439)) + { + printk(KERN_WARNING "ipt_time: invalid argument\n"); + return 0; + } + + return 1; +} + +static struct ipt_match time_match += { { NULL, NULL }, "time", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + printk("ipt_time loading\n"); + return ipt_register_match(&time_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&time_match); + printk("ipt_time unloaded\n"); +} + +module_init(init); +module_exit(fini); + + +/* The part below is borowed and modified from dietlibc */ + +/* seconds per day */ +#define SPD 24*60*60 + +void +localtime(const time_t *timepr, struct tm *r) { + time_t i; + time_t timep; + extern struct timezone sys_tz; + const unsigned int __spm[12] = + { 0, + (31), + (31+28), + (31+28+31), + (31+28+31+30), + (31+28+31+30+31), + (31+28+31+30+31+30), + (31+28+31+30+31+30+31), + (31+28+31+30+31+30+31+31), + (31+28+31+30+31+30+31+31+30), + (31+28+31+30+31+30+31+31+30+31), + (31+28+31+30+31+30+31+31+30+31+30), + }; + register time_t work; + + timep = (*timepr) - (sys_tz.tz_minuteswest * 60); + work=timep%(SPD); + r->tm_sec=work%60; work/=60; + r->tm_min=work%60; r->tm_hour=work/60; + work=timep/(SPD); + r->tm_wday=(4+work)%7; + for (i=1970; ; ++i) { + register time_t k= (!(i%4) && ((i%100) || !(i%400)))?366:365; + if (work>k) + work-=k; + else + break; + } + r->tm_year=i-1900; + for (i=11; i && __spm[i]>work; --i) ; + r->tm_mon=i; + r->tm_mday=work-__spm[i]+1; +} diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/Config.in linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/Config.in --- linux-2.4.18_vanilla/net/ipv6/netfilter/Config.in 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/Config.in 2002-07-07 00:19:40.000000000 +0200 @@ -18,12 +18,31 @@ # 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 + 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 ' 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 + fi # dep_tristate ' Multiple port match support' CONFIG_IP6_NF_MATCH_MULTIPORT $CONFIG_IP6_NF_IPTABLES # dep_tristate ' TOS match support' CONFIG_IP6_NF_MATCH_TOS $CONFIG_IP6_NF_IPTABLES # if [ "$CONFIG_IP6_NF_CONNTRACK" != "n" ]; then @@ -37,6 +56,9 @@ # The targets 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 ' LOG target support' CONFIG_IP6_NF_TARGET_LOG $CONFIG_IP6_NF_FILTER fi diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/Makefile linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/Makefile --- linux-2.4.18_vanilla/net/ipv6/netfilter/Makefile 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/Makefile 2002-07-07 00:19:40.000000000 +0200 @@ -15,12 +15,20 @@ 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_LENGTH) += ip6t_length.o obj-$(CONFIG_IP6_NF_MATCH_MAC) += ip6t_mac.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_REJECT) += ip6t_REJECT.o obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/ip6_queue.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6_queue.c --- linux-2.4.18_vanilla/net/ipv6/netfilter/ip6_queue.c 2002-02-25 21:38:14.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6_queue.c 2002-07-07 00:19:40.000000000 +0200 @@ -518,7 +518,7 @@ return netlink_unicast(nfnl, skb, nlq6->peer.pid, MSG_DONTWAIT); } -#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0); +#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) static __inline__ void netlink_receive_user_skb(struct sk_buff *skb) { diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_REJECT.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_REJECT.c --- linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_REJECT.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_REJECT.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,274 @@ +/* + * This is a module which is used for rejecting packets. + * Added support for customized reject packets (Jozsef Kadlecsik). + * Sun 12 Nov 2000 + * Port to IPv6 / ip6tables (Harald Welte ) + */ +#include +#include +#include +#include +#include +#include +#include + +#if 1 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +#if 0 +/* Send RST reply */ +static void send_reset(struct sk_buff *oldskb) +{ + struct sk_buff *nskb; + struct tcphdr *otcph, *tcph; + struct rtable *rt; + unsigned int otcplen; + int needs_ack; + + /* IP header checks: fragment, too short. */ + if (oldskb->nh.iph->frag_off & htons(IP_OFFSET) + || oldskb->len < (oldskb->nh.iph->ihl<<2) + sizeof(struct tcphdr)) + return; + + otcph = (struct tcphdr *)((u_int32_t*)oldskb->nh.iph + oldskb->nh.iph->ihl); + otcplen = oldskb->len - oldskb->nh.iph->ihl*4; + + /* No RST for RST. */ + if (otcph->rst) + return; + + /* Check checksum. */ + if (tcp_v4_check(otcph, otcplen, oldskb->nh.iph->saddr, + oldskb->nh.iph->daddr, + csum_partial((char *)otcph, otcplen, 0)) != 0) + return; + + /* Copy skb (even if skb is about to be dropped, we can't just + clone it because there may be other things, such as tcpdump, + interested in it) */ + nskb = skb_copy(oldskb, GFP_ATOMIC); + if (!nskb) + return; + + /* This packet will not be the same as the other: clear nf fields */ + nf_conntrack_put(nskb->nfct); + nskb->nfct = NULL; + nskb->nfcache = 0; +#ifdef CONFIG_NETFILTER_DEBUG + nskb->nf_debug = 0; +#endif + + tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl); + + nskb->nh.iph->daddr = xchg(&nskb->nh.iph->saddr, nskb->nh.iph->daddr); + tcph->source = xchg(&tcph->dest, tcph->source); + + /* Truncate to length (no data) */ + tcph->doff = sizeof(struct tcphdr)/4; + skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr)); + nskb->nh.iph->tot_len = htons(nskb->len); + + if (tcph->ack) { + needs_ack = 0; + tcph->seq = otcph->ack_seq; + tcph->ack_seq = 0; + } else { + needs_ack = 1; + tcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn + otcph->fin + + otcplen - (otcph->doff<<2)); + tcph->seq = 0; + } + + /* Reset flags */ + ((u_int8_t *)tcph)[13] = 0; + tcph->rst = 1; + tcph->ack = needs_ack; + + tcph->window = 0; + tcph->urg_ptr = 0; + + /* Adjust TCP checksum */ + tcph->check = 0; + tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr), + nskb->nh.iph->saddr, + nskb->nh.iph->daddr, + csum_partial((char *)tcph, + sizeof(struct tcphdr), 0)); + + /* Adjust IP TTL, DF */ + nskb->nh.iph->ttl = MAXTTL; + /* Set DF, id = 0 */ + nskb->nh.iph->frag_off = htons(IP_DF); + nskb->nh.iph->id = 0; + + /* Adjust IP checksum */ + nskb->nh.iph->check = 0; + nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, + nskb->nh.iph->ihl); + + /* Routing */ + if (ip_route_output(&rt, nskb->nh.iph->daddr, nskb->nh.iph->saddr, + RT_TOS(nskb->nh.iph->tos) | RTO_CONN, + 0) != 0) + goto free_nskb; + + dst_release(nskb->dst); + nskb->dst = &rt->u.dst; + + /* "Never happens" */ + if (nskb->len > nskb->dst->pmtu) + goto free_nskb; + + NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev, + ip_finish_output); + return; + + free_nskb: + kfree_skb(nskb); +} +#endif + +static unsigned int reject6_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ip6t_reject_info *reject = targinfo; + + /* WARNING: This code causes reentry within ip6tables. + This means that the ip6tables jump stack is now crap. We + must return an absolute verdict. --RR */ + DEBUGP("REJECTv6: calling icmpv6_send\n"); + switch (reject->with) { + case IP6T_ICMP6_NO_ROUTE: + icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_NOROUTE, 0, out); + break; + case IP6T_ICMP6_ADM_PROHIBITED: + icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_ADM_PROHIBITED, 0, out); + break; + case IP6T_ICMP6_NOT_NEIGHBOUR: + icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_NOT_NEIGHBOUR, 0, out); + break; + case IP6T_ICMP6_ADDR_UNREACH: + icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, out); + break; + case IP6T_ICMP6_PORT_UNREACH: + icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, out); + break; +#if 0 + case IPT_ICMP_ECHOREPLY: { + struct icmp6hdr *icmph = (struct icmphdr *) + ((u_int32_t *)(*pskb)->nh.iph + (*pskb)->nh.iph->ihl); + unsigned int datalen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4; + + /* Not non-head frags, or truncated */ + if (((ntohs((*pskb)->nh.iph->frag_off) & IP_OFFSET) == 0) + && datalen >= 4) { + /* Usually I don't like cut & pasting code, + but dammit, my party is starting in 45 + mins! --RR */ + struct icmp_bxm icmp_param; + + icmp_param.icmph=*icmph; + icmp_param.icmph.type=ICMP_ECHOREPLY; + icmp_param.data_ptr=(icmph+1); + icmp_param.data_len=datalen; + icmp_reply(&icmp_param, *pskb); + } + } + break; + case IPT_TCP_RESET: + send_reset(*pskb); + break; +#endif + default: + printk(KERN_WARNING "REJECTv6: case %u not handled yet\n", reject->with); + break; + } + + return NF_DROP; +} + +static inline int find_ping_match(const struct ip6t_entry_match *m) +{ + const struct ip6t_icmp *icmpinfo = (const struct ip6t_icmp *)m->data; + + if (strcmp(m->u.kernel.match->name, "icmp6") == 0 + && icmpinfo->type == ICMPV6_ECHO_REQUEST + && !(icmpinfo->invflags & IP6T_ICMP_INV)) + return 1; + + return 0; +} + +static int check(const char *tablename, + const struct ip6t_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + const struct ip6t_reject_info *rejinfo = targinfo; + + if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_reject_info))) { + DEBUGP("REJECTv6: targinfosize %u != 0\n", targinfosize); + return 0; + } + + /* Only allow these for packet filtering. */ + if (strcmp(tablename, "filter") != 0) { + DEBUGP("REJECTv6: bad table `%s'.\n", tablename); + return 0; + } + if ((hook_mask & ~((1 << NF_IP6_LOCAL_IN) + | (1 << NF_IP6_FORWARD) + | (1 << NF_IP6_LOCAL_OUT))) != 0) { + DEBUGP("REJECTv6: bad hook mask %X\n", hook_mask); + return 0; + } + + if (rejinfo->with == IP6T_ICMP6_ECHOREPLY) { + /* Must specify that it's an ICMP ping packet. */ + if (e->ipv6.proto != IPPROTO_ICMPV6 + || (e->ipv6.invflags & IP6T_INV_PROTO)) { + DEBUGP("REJECTv6: ECHOREPLY illegal for non-icmp\n"); + return 0; + } + /* Must contain ICMP match. */ + if (IP6T_MATCH_ITERATE(e, find_ping_match) == 0) { + DEBUGP("REJECTv6: ECHOREPLY illegal for non-ping\n"); + return 0; + } + } else if (rejinfo->with == IP6T_TCP_RESET) { + /* Must specify that it's a TCP packet */ + if (e->ipv6.proto != IPPROTO_TCP + || (e->ipv6.invflags & IP6T_INV_PROTO)) { + DEBUGP("REJECTv6: TCP_RESET illegal for non-tcp\n"); + return 0; + } + } + + return 1; +} + +static struct ip6t_target ip6t_reject_reg += { { NULL, NULL }, "REJECT", reject6_target, check, NULL, THIS_MODULE }; + +static int __init init(void) +{ + if (ip6t_register_target(&ip6t_reject_reg)) + return -EINVAL; + return 0; +} + +static void __exit fini(void) +{ + ip6t_unregister_target(&ip6t_reject_reg); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_ah.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_ah.c --- linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_ah.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_ah.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,218 @@ +/* Kernel module to match AH parameters. */ +#include +#include +#include +#include +#include +#include + +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPv6 AH match"); +MODULE_AUTHOR("Andras Kis-Szabo "); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +struct ahhdr { + __u8 nexthdr; + __u8 hdrlen; + __u16 reserved; + __u32 spi; +}; + +int ipv6_ext_hdr(u8 nexthdr) +{ + return ( (nexthdr == NEXTHDR_HOP) || + (nexthdr == NEXTHDR_ROUTING) || + (nexthdr == NEXTHDR_FRAGMENT) || + (nexthdr == NEXTHDR_AUTH) || + (nexthdr == NEXTHDR_ESP) || + (nexthdr == NEXTHDR_NONE) || + (nexthdr == NEXTHDR_DEST) ); +} + +/* 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 (ipv6_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=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=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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_dst.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_dst.c --- linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_dst.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_dst.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,287 @@ +/* Kernel module to match Hop-by-Hop and Destination parameters. */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 "); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +int ipv6_ext_hdr(u8 nexthdr) +{ + return ( (nexthdr == NEXTHDR_HOP) || + (nexthdr == NEXTHDR_ROUTING) || + (nexthdr == NEXTHDR_FRAGMENT) || + (nexthdr == NEXTHDR_AUTH) || + (nexthdr == NEXTHDR_ESP) || + (nexthdr == NEXTHDR_NONE) || + (nexthdr == NEXTHDR_DEST) ); +} + +/* + * (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 (ipv6_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; tempoptsnr; 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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_esp.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_esp.c --- linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_esp.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_esp.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,186 @@ +/* Kernel module to match ESP parameters. */ +#include +#include +#include +#include +#include +#include + +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPv6 ESP match"); +MODULE_AUTHOR("Andras Kis-Szabo "); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +struct esphdr { + __u32 spi; +}; + +int ipv6_ext_hdr(u8 nexthdr) +{ + return ( (nexthdr == NEXTHDR_HOP) || + (nexthdr == NEXTHDR_ROUTING) || + (nexthdr == NEXTHDR_FRAGMENT) || + (nexthdr == NEXTHDR_AUTH) || + (nexthdr == NEXTHDR_ESP) || + (nexthdr == NEXTHDR_NONE) || + (nexthdr == NEXTHDR_DEST) ); +} + +/* 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 (ipv6_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=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=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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_eui64.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_eui64.c --- linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_eui64.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_eui64.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,89 @@ +/* Kernel module to match EUI64 address parameters. */ +#include +#include +#include +#include + +#include + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + + unsigned char eui64[8]; + int i=0; + + if ( !(skb->mac.raw >= skb->head + && (skb->mac.raw + ETH_HLEN) <= skb->data) + && offset != 0) { + *hotdrop = 1; + return 0; + } + + memset(eui64, 0, sizeof(eui64)); + + if (skb->mac.ethernet->h_proto == ntohs(ETH_P_IPV6)) { + if (skb->nh.ipv6h->version == 0x6) { + memcpy(eui64, skb->mac.ethernet->h_source, 3); + memcpy(eui64 + 5, skb->mac.ethernet->h_source + 3, 3); + eui64[3]=0xff; + eui64[4]=0xfe; + eui64[0] |= 0x02; + + i=0; + while ((skb->nh.ipv6h->saddr.in6_u.u6_addr8[8+i] == + eui64[i]) && (i<8)) i++; + + if ( i == 8 ) + return 1; + } + } + + return 0; +} + +static int +ip6t_eui64_checkentry(const char *tablename, + const struct ip6t_ip6 *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (hook_mask + & ~((1 << NF_IP6_PRE_ROUTING) | (1 << NF_IP6_LOCAL_IN) | + (1 << NF_IP6_PRE_ROUTING) )) { + printk("ip6t_eui64: only valid for PRE_ROUTING, LOCAL_IN or FORWARD.\n"); + return 0; + } + + if (matchsize != IP6T_ALIGN(sizeof(int))) + return 0; + + return 1; +} + +static struct ip6t_match eui64_match += { { NULL, NULL }, "eui64", &match, &ip6t_eui64_checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ip6t_register_match(&eui64_match); +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&eui64_match); +} + +module_init(init); +module_exit(fini); +MODULE_DESCRIPTION("IPv6 EUI64 address checking match"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andras Kis-Szabo "); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_frag.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_frag.c --- linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_frag.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_frag.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,250 @@ +/* Kernel module to match FRAG parameters. */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPv6 FRAG match"); +MODULE_AUTHOR("Andras Kis-Szabo "); + +#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; +}; + +int ipv6_ext_hdr(u8 nexthdr) +{ + return ( (nexthdr == NEXTHDR_HOP) || + (nexthdr == NEXTHDR_ROUTING) || + (nexthdr == NEXTHDR_FRAGMENT) || + (nexthdr == NEXTHDR_AUTH) || + (nexthdr == NEXTHDR_ESP) || + (nexthdr == NEXTHDR_NONE) || + (nexthdr == NEXTHDR_DEST) ); +} + +/* 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 (ipv6_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=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=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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_hbh.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_hbh.c --- linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_hbh.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_hbh.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,287 @@ +/* Kernel module to match Hop-by-Hop and Destination parameters. */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 "); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +int ipv6_ext_hdr(u8 nexthdr) +{ + return ( (nexthdr == NEXTHDR_HOP) || + (nexthdr == NEXTHDR_ROUTING) || + (nexthdr == NEXTHDR_FRAGMENT) || + (nexthdr == NEXTHDR_AUTH) || + (nexthdr == NEXTHDR_ESP) || + (nexthdr == NEXTHDR_NONE) || + (nexthdr == NEXTHDR_DEST) ); +} + +/* + * (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 (ipv6_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; tempoptsnr; 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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_ipv6header.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_ipv6header.c --- linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_ipv6header.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_ipv6header.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,222 @@ +/* ipv6header match - matches IPv6 packets based +on whether they contain certain headers */ + +/* Original idea: Brad Chapman + * Rewritten by: Andras Kis-Szabo */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPv6 headers match"); +MODULE_AUTHOR("Andras Kis-Szabo "); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +int ipv6_ext_hdr(u8 nexthdr) +{ + return ( (nexthdr == NEXTHDR_HOP) || + (nexthdr == NEXTHDR_ROUTING) || + (nexthdr == NEXTHDR_FRAGMENT) || + (nexthdr == NEXTHDR_AUTH) || + (nexthdr == NEXTHDR_ESP) || + (nexthdr == NEXTHDR_NONE) || + (nexthdr == NEXTHDR_DEST) ); +} + +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; + struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; + + /* Make sure this isn't an evil packet */ + DEBUGP("ipv6_header 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; + + DEBUGP("ipv6_header nexthdr %02X \n",nexthdr); + DEBUGP("ipv6_header ptr %08X \n",ptr); + DEBUGP("ipv6_header skblen %04X \n",skb->len); + DEBUGP("ipv6_header skbdatalen %04X \n",skb->data_len); + DEBUGP("ipv6_header len %04X \n",len); +#if 0 + for (temp=0;templen;temp++){ + if (!(temp % 16 )) DEBUGP("\nipv6_header data "); + DEBUGP("%02X ",skb->data[temp]); + } +#endif + DEBUGP("\nipv6_header h.raw %02X %02X %02X %02X \n", + skb->h.raw[0], + skb->h.raw[1], + skb->h.raw[2], + skb->h.raw[3]); + DEBUGP("ipv6_header nh.raw %02X %02X %02X %02X \n", + skb->nh.raw[0], + skb->nh.raw[1], + skb->nh.raw[2], + skb->nh.raw[3]); + DEBUGP("ipv6_header CB %02X %02X %02X %02X %02X %02X %02X \n", + opt->iif, + opt->ra, + opt->hop, + opt->auth, + opt->dst0, + opt->srcrt, + opt->dst1); + + temp = 0; + + while (ipv6_ext_hdr(nexthdr)) { + struct ipv6_opt_hdr *hdr; + int hdrlen; + + DEBUGP("ipv6_header 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) { + temp |= MASK_NONE; + break; + } + /* ESP -> evaluate */ + if (nexthdr == NEXTHDR_ESP) { + temp |= MASK_ESP; + break; + } + + 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); + + DEBUGP("ipv6_header hdrlen %04X \n",hdrlen); + + /* 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: + DEBUGP("IPV6HEADER match: unknown nextheader %u\n",nexthdr); + return 0; + break; + } + + nexthdr = hdr->nexthdr; + len -= hdrlen; + ptr += hdrlen; + if ( ptr > skb->len ) { + DEBUGP("ipv6_header new ptr %04X \n",ptr); + break; + } + } + + if ( (nexthdr != NEXTHDR_NONE ) && (nexthdr != NEXTHDR_ESP) ) + temp |= MASK_PROTO; + + DEBUGP ("ipv6header: %02X %02X \n", temp, info->matchflags); + + 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))) { + DEBUGP("ip6t_ipv6header: matchsize != %u\n", + IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info))); + return 0; + } + + return 1; +} + +static void +ipv6header_destroy(void *matchinfo, + unsigned int matchinfosize) +{ + return; +} + +static struct ip6t_match +ip6t_ipv6header_match = { + { NULL, NULL }, + "ipv6header", + &ipv6header_match, + &ipv6header_checkentry, + &ipv6header_destroy, + 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 -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_length.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_length.c --- linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_length.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_length.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,51 @@ +/* Length Match - IPv6 Port */ + +#include +#include +#include +#include + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ip6t_length_info *info = matchinfo; + u_int16_t pktlen = ntohs(skb->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr); + + return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; +} + +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_length_info))) + return 0; + + return 1; +} + +static struct ip6t_match length_match += { { NULL, NULL }, "length", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ip6t_register_match(&length_match); +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&length_match); +} + +module_init(init); +module_exit(fini); diff -u --new-file --recursive linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_rt.c linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_rt.c --- linux-2.4.18_vanilla/net/ipv6/netfilter/ip6t_rt.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.18_iptables-1.2.7-20020511/net/ipv6/netfilter/ip6t_rt.c 2002-07-07 00:19:40.000000000 +0200 @@ -0,0 +1,305 @@ +/* Kernel module to match ROUTING parameters. */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPv6 RT match"); +MODULE_AUTHOR("Andras Kis-Szabo "); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +int ipv6_ext_hdr(u8 nexthdr) +{ + return ( (nexthdr == NEXTHDR_HOP) || + (nexthdr == NEXTHDR_ROUTING) || + (nexthdr == NEXTHDR_FRAGMENT) || + (nexthdr == NEXTHDR_AUTH) || + (nexthdr == NEXTHDR_ESP) || + (nexthdr == NEXTHDR_NONE) || + (nexthdr == NEXTHDR_DEST) ); +} + +/* 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 (ipv6_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=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=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; tempaddrnr; 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, +