diff -urN linux-2.4.26/Documentation/Configure.help linux-2.4.26-pomng/Documentation/Configure.help --- linux-2.4.26/Documentation/Configure.help Sat Apr 17 23:05:48 2004 +++ linux-2.4.26-pomng/Documentation/Configure.help Sun Apr 18 17:33:11 2004 @@ -2770,6 +2770,14 @@ Documentation/modules.txt. If unsure, say `N'. +RTSP protocol support +CONFIG_IP_NF_RTSP + Support the RTSP protocol. This allows UDP transports to be setup + properly, including RTP and RDT. + + If you want to compile it as a module, say 'M' here and read + Documentation/modules.txt. If unsure, say 'Y'. + IRC Send/Chat protocol support CONFIG_IP_NF_IRC There is a commonly-used extension to IRC called @@ -2784,6 +2792,37 @@ If you want to compile it as a module, say 'M' here and read Documentation/modules.txt. If unsure, say 'N'. +Quake III Arena protocol support +CONFIG_IP_NF_QUAKE3 + Quake III Arena connection tracking helper. This module allows for a + stricter firewall rulebase if one only allows traffic to a master + server. Connections to Quake III server IP addresses and ports returned + by the master server will be tracked automatically. + + If you want to compile it as a module, say M here and read + . If unsure, say `Y'. + +MMS protocol support +CONFIG_IP_NF_MMS + Tracking MMS (Microsoft Windows Media Services) connections + could be problematic if random ports are used to send the + streaming content. This option allows users to track streaming + connections over random UDP or TCP ports. + + If you want to compile it as a module, say M here and read + . If unsure, say `Y'. + +CuSeeMe protocol support +CONFIG_IP_NF_CUSEEME + The CuSeeMe conferencing protocol is problematic when used in + conjunction with NAT; even though there are no random ports used for + extra connections, the messages contain IP addresses inside them. + This NAT helper mangles the IP address inside packets so both + parties don't get confused. + + If you want to compile it as a module, say M here and read + . If unsure, say `Y'. + TFTP protocol support CONFIG_IP_NF_TFTP TFTP connection tracking helper, this is required depending @@ -2803,6 +2842,61 @@ If you want to compile it as a module, say M here and read . If unsure, say `Y'. +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'. + +RSH protocol support +CONFIG_IP_NF_RSH + The RSH connection tracker is required if the dynamic + stderr "Server to Client" connection is to occur during a + normal RSH session. This typically operates as follows; + + Client 0:1023 --> Server 514 (stream 1 - stdin/stdout) + Client 0:1023 <-- Server 0:1023 (stream 2 - stderr) + + This connection tracker will identify new RSH sessions, + extract the outbound session details, and notify netfilter + of pending "related" sessions. + + Warning: This module could be dangerous. It is not "best + practice" to use RSH, use SSH in all instances. + (see rfc1244, rfc1948, rfc2179, etc ad-nauseum) + + + If you want to compile it as a module, say M here and read + . 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'. + +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'. + User space queueing via NETLINK CONFIG_IP_NF_QUEUE Netfilter has the ability to queue packets to user space: the @@ -2841,6 +2935,14 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +addrtype match support +CONFIG_IP_NF_MATCH_ADDRTYPE + This option allows you to match what routing thinks of an address, + eg. UNICAST, LOCAL, BROADCAST, ... + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + skb->pkt_type packet match support CONFIG_IP_NF_MATCH_PKTTYPE This patch allows you to match packet in accrodance @@ -2878,6 +2980,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'. + TTL match support CONFIG_IP_NF_MATCH_TTL This adds CONFIG_IP_NF_MATCH_TTL option, which enabled the user @@ -2894,6 +3004,17 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +U32 patch support +CONFIG_IP_NF_MATCH_U32 + U32 allows you to extract quantities of up to 4 bytes from a packet, + AND them with specified masks, shift them by specified amounts and + test whether the results are in any of a set of specified ranges. + The specification of what to extract is general enough to skip over + headers with lengths stored in the packet, as in IP or TCP header + lengths. + + Details and examples are in the kernel module source. + AH/ESP match support CONFIG_IP_NF_MATCH_AH_ESP These two match extensions (`ah' and `esp') allow you to match a @@ -2932,6 +3053,73 @@ If you want to compile it as a module, say M here and read . 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'. + +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'. + +OSF match support +CONFIG_IP_NF_MATCH_OSF + + The idea of passive OS fingerprint matching exists for quite a long time, + but was created as extension fo OpenBSD pf only some weeks ago. + Original idea was lurked in some OpenBSD mailing list (thanks + grange@open...) and than adopted for Linux netfilter in form of this code. + + Original table was created by Michal Zalewski for + his excellent p0f and than changed a bit for more convenience. + + This module compares some data(WS, MSS, options and it's order, ttl, + df and others) from first SYN packet (actually from packets with SYN + bit set) with hardcoded in fingers[] table ones. + + If you say Y here, try iptables -m osf --help for more information. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +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'. + +iprange match support +CONFIG_IP_NF_MATCH_IPRANGE + This option makes possible to match IP addresses against + IP address ranges. + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +Condition variable match support +CONFIG_IP_NF_MATCH_CONDITION + This option allows you to match firewall rules against condition + variables stored in the /proc/net/ipt_condition directory. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + conntrack match support CONFIG_IP_NF_MATCH_CONNTRACK This is a general conntrack match module, a superset of the state match. @@ -2953,6 +3141,44 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +RPC match support +CONFIG_IP_NF_MATCH_RPC + This adds CONFIG_IP_NF_MATCH_RPC, which is the RPC connection + matcher and tracker. + + This option supplies two connection tracking modules; + ip_conntrack_rpc_udp and ip_conntrack_rpc_tcp, which track + portmapper requests using UDP and TCP respectively. + + This option also adds an RPC match module for iptables, which + matches both via the old "record match" method and a new + "procedure match" method. The older method matches all RPC + procedure packets that relate to previously recorded packets + seen querying a portmapper. The newer method matches only + those RPC procedure packets explicitly specified by the user, + and that can then be related to previously recorded packets + seen querying a portmapper. + + These three modules are required if RPCs are to be filtered + accurately; as RPCs are allocated pseudo-randomly to UDP and + TCP ports as they register with the portmapper. + + Up to 8 portmapper ports per module, and up to 128 RPC + procedures per iptables rule, may be specified by the user, + to enable effective RPC management. + + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +Connections/IP limit match support +CONFIG_IP_NF_MATCH_CONNLIMIT + This match allows you to restrict the number of parallel TCP + connections to a server per client IP address (or address block). + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + Unclean match support CONFIG_IP_NF_MATCH_UNCLEAN Unclean packet matching matches any strange or invalid packets, by @@ -2961,6 +3187,14 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +String match support (EXPERIMENTAL) +CONFIG_IP_NF_MATCH_STRING + String matching alows you to match packets which contain a + specified string of characters. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + Owner match support CONFIG_IP_NF_MATCH_OWNER Packet owner matching allows you to match locally-generated packets @@ -2978,6 +3212,21 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +TARPIT target support +CONFIG_IP_NF_TARGET_TARPIT + Adds a TARPIT target to iptables, which captures and holds + incoming TCP connections using no local per-connection resources. + Connections are accepted, but immediately switched to the persist + state (0 byte window), in which the remote side stops sending data + and asks to continue every 60-240 seconds. Attempts to close the + connection are ignored, forcing the remote side to time out the + connection in 12-24 minutes. + + This offers similar functionality to LaBrea + but doesn't require dedicated + hardware or IPs. Any TCP port that you would normally DROP or REJECT + can instead become a tarpit. + REJECT target support CONFIG_IP_NF_TARGET_REJECT The REJECT target allows a filtering rule to specify that an ICMP @@ -2987,6 +3236,22 @@ If you want to compile it as a module, say M here and read . 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. + +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'. + MIRROR target support CONFIG_IP_NF_TARGET_MIRROR The MIRROR target allows a filtering rule to specify that an @@ -3052,6 +3317,27 @@ If you want to compile it as a module, say M here and read . 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'. + +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'. + Packet mangling CONFIG_IP_NF_MANGLE This option adds a `mangle' table to iptables: see the man page for @@ -3112,6 +3398,21 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +ROUTE target support +CONFIG_IP_NF_TARGET_ROUTE + This option adds a `ROUTE' target, which enables you to setup unusual + routes. For example, the ROUTE lets you route a received packet through + an interface or towards a host, even if the regular destination of the + packet is the router itself. The ROUTE target is also able to change the + incoming interface of a packet. + + The target can be or not a final target. It has to be used inside the + mangle table. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. The module will be called ipt_ROUTE.o. + If unsure, say `N'. + TCPMSS target support CONFIG_IP_NF_TARGET_TCPMSS This option adds a `TCPMSS' target, which allows you to alter the @@ -3175,6 +3476,98 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +IP address/port set (ipset) support +CONFIG_IP_NF_SET + This option adds IP address/port set (i.e. ipset) support to the kernel. + It will enable the `set' match and `SET' target support in netfilter + as well. + + In order to define and use sets, you need userlevel utilities: a patches + iptables and the program ipset(8), which defines the sets and their + bounds. + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +ipmap set type support +CONFIG_IP_NF_SET_IPMAP + This option adds the ipmap set type support. + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +macipmap set type support +CONFIG_IP_NF_SET_MACIPMAP + This option adds the macipmap set type support. + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +portmap set type support +CONFIG_IP_NF_SET_PORTMAP + This option adds the portmap set type support. + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +iphash set type support +CONFIG_IP_NF_SET_IPHASH + This option adds the iphash set type support. + + If you want to compile it as a module, say M here and read + . 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. + +XOR target support +CONFIG_IP_NF_TARGET_XOR + This option adds a `XOR' target, which can encrypt TCP and + UDP traffic using a simple XOR encryption. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +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'. + +CLASSIFY target support +CONFIG_IP_NF_TARGET_CLASSIFY + This option adds a `CLASSIFY' target, which enables the user to set + the priority of a packet. Some qdiscs can use this value for classification, + among these are: + + atm, cbq, dsmark, pfifo_fast, htb, prio + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + ipchains (2.2-style) support CONFIG_IP_NF_COMPAT_IPCHAINS This option places ipchains (with masquerading and redirection @@ -3214,6 +3607,68 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +Random match support +CONFIG_IP6_NF_MATCH_RANDOM + This option adds a `random' match, + which allow you to match packets randomly + following a given probability. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +Random match support +CONFIG_IP6_NF_MATCH_RANDOM + This option adds a `random' match, + which allow you to match packets randomly + following a given probability. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +Nth match support +CONFIG_IP6_NF_MATCH_NTH + This option adds a `Nth' match, which allow you to make + rules that match every Nth packet. By default there are + 16 different counters. + +[options] + --every Nth Match every Nth packet + [--counter] num Use counter 0-15 (default:0) + [--start] num Initialize the counter at the number 'num' + instead of 0. Must be between 0 and Nth-1 + [--packet] num Match on 'num' packet. Must be between 0 + and Nth-1. + + If --packet is used for a counter than + there must be Nth number of --packet + rules, covering all values between 0 and + Nth-1 inclusively. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +Nth match support +CONFIG_IP6_NF_MATCH_NTH + This option adds a `Nth' match, which allow you to make + rules that match every Nth packet. By default there are + 16 different counters. + +[options] + --every Nth Match every Nth packet + [--counter] num Use counter 0-15 (default:0) + [--start] num Initialize the counter at the number 'num' + instead of 0. Must be between 0 and Nth-1 + [--packet] num Match on 'num' packet. Must be between 0 + and Nth-1. + + If --packet is used for a counter than + there must be Nth number of --packet + rules, covering all values between 0 and + Nth-1 inclusively. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + length match support CONFIG_IP6_NF_MATCH_LENGTH This option allows you to match the length of a packet against a @@ -3231,6 +3686,14 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +Condition variable match support +CONFIG_IP6_NF_MATCH_CONDITION + This option allows you to match firewall rules against condition + variables stored in the /proc/net/ipt_condition directory. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + Multiple port match support CONFIG_IP6_NF_MATCH_MULTIPORT Multiport matching allows you to match TCP or UDP packets based on @@ -3274,6 +3737,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 mangling CONFIG_IP6_NF_MANGLE This option adds a `mangle' table to iptables: see the man page for @@ -3294,6 +3766,17 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. + +ROUTE target support +CONFIG_IP6_NF_TARGET_ROUTE + This option adds a `ROUTE' target, which enables you to setup unusual + routes. The ROUTE target is also able to change the incoming interface + of a packet. + + The target can be or not a final target. It has to be used inside the + mangle table. + + Not working as a module. ARP tables support CONFIG_IP_NF_ARPTABLES diff -urN linux-2.4.26/include/linux/netfilter_helpers.h linux-2.4.26-pomng/include/linux/netfilter_helpers.h --- linux-2.4.26/include/linux/netfilter_helpers.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_helpers.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,133 @@ +/* + * Helpers for netfiler modules. This file provides implementations for basic + * functions such as strncasecmp(), etc. + * + * gcc will warn for defined but unused functions, so we only include the + * functions requested. The following macros are used: + * NF_NEED_STRNCASECMP nf_strncasecmp() + * NF_NEED_STRTOU16 nf_strtou16() + * NF_NEED_STRTOU32 nf_strtou32() + */ +#ifndef _NETFILTER_HELPERS_H +#define _NETFILTER_HELPERS_H + +/* Only include these functions for kernel code. */ +#ifdef __KERNEL__ + +#include +#define iseol(c) ( (c) == '\r' || (c) == '\n' ) + +/* + * The standard strncasecmp() + */ +#ifdef NF_NEED_STRNCASECMP +static int +nf_strncasecmp(const char* s1, const char* s2, u_int32_t len) +{ + if (s1 == NULL || s2 == NULL) + { + if (s1 == NULL && s2 == NULL) + { + return 0; + } + return (s1 == NULL) ? -1 : 1; + } + while (len > 0 && tolower(*s1) == tolower(*s2)) + { + len--; + s1++; + s2++; + } + return ( (len == 0) ? 0 : (tolower(*s1) - tolower(*s2)) ); +} +#endif /* NF_NEED_STRNCASECMP */ + +/* + * Parse a string containing a 16-bit unsigned integer. + * Returns the number of chars used, or zero if no number is found. + */ +#ifdef NF_NEED_STRTOU16 +static int +nf_strtou16(const char* pbuf, u_int16_t* pval) +{ + int n = 0; + + *pval = 0; + while (isdigit(pbuf[n])) + { + *pval = (*pval * 10) + (pbuf[n] - '0'); + n++; + } + + return n; +} +#endif /* NF_NEED_STRTOU16 */ + +/* + * Parse a string containing a 32-bit unsigned integer. + * Returns the number of chars used, or zero if no number is found. + */ +#ifdef NF_NEED_STRTOU32 +static int +nf_strtou32(const char* pbuf, u_int32_t* pval) +{ + int n = 0; + + *pval = 0; + while (pbuf[n] >= '0' && pbuf[n] <= '9') + { + *pval = (*pval * 10) + (pbuf[n] - '0'); + n++; + } + + return n; +} +#endif /* NF_NEED_STRTOU32 */ + +/* + * Given a buffer and length, advance to the next line and mark the current + * line. + */ +#ifdef NF_NEED_NEXTLINE +static int +nf_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen) +{ + uint off = *poff; + uint physlen = 0; + + if (off >= len) + { + return 0; + } + + while (p[off] != '\n') + { + if (len-off <= 1) + { + return 0; + } + + physlen++; + off++; + } + + /* if we saw a crlf, physlen needs adjusted */ + if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r') + { + physlen--; + } + + /* advance past the newline */ + off++; + + *plineoff = *poff; + *plinelen = physlen; + *poff = off; + + return 1; +} +#endif /* NF_NEED_NEXTLINE */ + +#endif /* __KERNEL__ */ + +#endif /* _NETFILTER_HELPERS_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack.h Sat Apr 17 23:05:49 2004 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack.h Sun Apr 18 17:33:11 2004 @@ -63,6 +63,11 @@ }; /* Add protocol helper include file here */ +#include +#include +#include +#include +#include #include #include @@ -71,6 +76,11 @@ /* per expectation: application helper private data */ union ip_conntrack_expect_help { /* insert conntrack helper private data (expect) here */ + struct ip_ct_talk_expect exp_talk_info; + struct ip_ct_rtsp_expect exp_rtsp_info; + struct ip_ct_rsh_expect exp_rsh_info; + struct ip_ct_mms_expect exp_mms_info; + struct ip_ct_h225_expect exp_h225_info; struct ip_ct_amanda_expect exp_amanda_info; struct ip_ct_ftp_expect exp_ftp_info; struct ip_ct_irc_expect exp_irc_info; @@ -85,6 +95,11 @@ /* per conntrack: application helper private data */ union ip_conntrack_help { /* insert conntrack helper private data (master) here */ + struct ip_ct_talk_master ct_talk_info; + struct ip_ct_rtsp_master ct_rtsp_info; + struct ip_ct_rsh_master ct_rsh_info; + struct ip_ct_mms_master ct_mms_info; + struct ip_ct_h225_master ct_h225_info; struct ip_ct_ftp_master ct_ftp_info; struct ip_ct_irc_master ct_irc_info; }; @@ -127,11 +142,11 @@ /* expectation list for this master */ struct list_head expected_list; - /* The conntrack of the master connection */ + /* The conntrack of the master connection: we hold a reference to it */ struct ip_conntrack *expectant; /* The conntrack of the sibling connection, set after - * expectation arrived */ + * expectation arrived: it holds a reference to us */ struct ip_conntrack *sibling; /* Tuple saved for conntrack */ @@ -167,6 +182,13 @@ /* These are my tuples; original and reply */ struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; + /* Hash keys to avoid recalculations. */ +#ifdef CONFIG_IP_NF_NAT_NEEDED + u_int32_t key[IP_CT_DIR_MAX]; +#else + u_int32_t key[IP_CT_DIR_REPLY]; +#endif + /* Have we seen traffic both ways yet? (bitset) */ unsigned long status; @@ -248,6 +270,11 @@ /* These are for NAT. Icky. */ /* Call me when a conntrack is destroyed. */ extern void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack); + +/* Update TCP window tracking data when NAT mangles the packet */ +extern void ip_conntrack_tcp_update(struct sk_buff *skb, + struct ip_conntrack *conntrack, + int dir); /* Returns new sk_buff, or NULL */ struct sk_buff * diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_core.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_core.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_core.h Wed Mar 31 19:52:16 2004 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_core.h Sun Apr 18 17:33:05 2004 @@ -17,8 +17,6 @@ struct ip_conntrack_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 *__ip_ct_find_proto(u_int8_t protocol); extern struct list_head protocol_list; /* Returns conntrack if it dealt with ICMP, and filled in skb->nfct */ @@ -45,8 +43,95 @@ return NF_ACCEPT; } -extern struct list_head *ip_conntrack_hash; +struct ip_conntrack_hash +{ + struct list_head list; + rwlock_t lock; +}; + +extern struct ip_conntrack_hash *ip_conntrack_hash; extern struct list_head ip_conntrack_expect_list; DECLARE_RWLOCK_EXTERN(ip_conntrack_lock); +DECLARE_RWLOCK_EXTERN(ip_conntrack_expect_lock); + +#ifdef CONFIG_IP_NF_NAT_NEEDED +static inline void write_lock_key(u_int32_t hash_a, u_int32_t hash_b) +{ + if (hash_a < hash_b) { + WRITE_LOCK(&ip_conntrack_hash[hash_a].lock); + WRITE_LOCK(&ip_conntrack_hash[hash_b].lock); + } else if (hash_b < hash_a) { + WRITE_LOCK(&ip_conntrack_hash[hash_b].lock); + WRITE_LOCK(&ip_conntrack_hash[hash_a].lock); + } else + WRITE_LOCK(&ip_conntrack_hash[hash_a].lock); +} + +static inline void write_unlock_key(u_int32_t hash_a, u_int32_t hash_b) +{ + if (hash_a < hash_b) { + WRITE_UNLOCK(&ip_conntrack_hash[hash_b].lock); + WRITE_UNLOCK(&ip_conntrack_hash[hash_a].lock); + } else if (hash_b < hash_a) { + WRITE_UNLOCK(&ip_conntrack_hash[hash_a].lock); + WRITE_UNLOCK(&ip_conntrack_hash[hash_b].lock); + } else + WRITE_UNLOCK(&ip_conntrack_hash[hash_a].lock); +} + +static inline void write_lock_ct(struct ip_conntrack *ct) +{ + write_lock_key(ct->key[IP_CT_DIR_ORIGINAL], + ct->key[IP_CT_DIR_REPLY]); +} + +static inline void write_unlock_ct(struct ip_conntrack *ct) +{ + write_unlock_key(ct->key[IP_CT_DIR_ORIGINAL], + ct->key[IP_CT_DIR_REPLY]); +} +#else +static inline void write_lock_key(u_int32_t hash_a, u_int32_t hash_b) +{ + WRITE_LOCK(&ip_conntrack_hash[hash_a].lock); +} + +static inline void write_unlock_key(u_int32_t hash_a, u_int32_t hash_b) +{ + WRITE_UNLOCK(&ip_conntrack_hash[hash_a].lock); +} + +static inline void write_lock_ct(struct ip_conntrack *ct) +{ + write_lock_key(ct->key[IP_CT_DIR_ORIGINAL], + ct->key[IP_CT_DIR_ORIGINAL]); +} + +static inline void write_unlock_ct(struct ip_conntrack *ct) +{ + write_unlock_key(ct->key[IP_CT_DIR_ORIGINAL], + ct->key[IP_CT_DIR_ORIGINAL]); +} +#endif /* CONFIG_IP_NF_NAT_NEEDED */ + +static inline void read_lock_key(u_int32_t hash_a) +{ + READ_LOCK(&ip_conntrack_hash[hash_a].lock); +} + +static inline void read_unlock_key(u_int32_t hash_a) +{ + READ_UNLOCK(&ip_conntrack_hash[hash_a].lock); +} + +static inline void read_lock_ct(const struct ip_conntrack *ct) +{ + read_lock_key(ct->key[IP_CT_DIR_ORIGINAL]); +} + +static inline void read_unlock_ct(const struct ip_conntrack *ct) +{ + read_unlock_key(ct->key[IP_CT_DIR_ORIGINAL]); +} #endif /* _IP_CONNTRACK_CORE_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h Sun Apr 18 17:33:06 2004 @@ -0,0 +1,70 @@ +#ifndef _IP_CT_CUSEEME +#define _IP_CT_CUSEEME + +#define CUSEEME_PORT 7648 + +/* These structs come from the 2.2 ip_masq_cuseeme code... */ + +#pragma pack(1) +/* CuSeeMe data header */ +struct cu_header { + u_int16_t dest_family; + u_int16_t dest_port; + u_int32_t dest_addr; + int16_t family; + u_int16_t port; + u_int32_t addr; + u_int32_t seq; + u_int16_t msg; + u_int16_t data_type; + /* possible values: + * 1 small video + * 2 big video + * 3 audio + * 100 acknowledge connectivity when there + * is nothing else to send + * 101 OpenContinue packet + * 104 display a text message and + * disconnect (used by reflector to + * kick clients off) + * 105 display a text message (welcome + * message from reflector) + * 106 exchanged among reflectors for + * reflector interoperation + * 107 carry aux stream data when there is + * no video to piggy-back on + * 108 obsolete (used in Mac alpha version) + * 109 obsolete (used in Mac alpha version) + * 110 used for data rate control + * 111 used for data rate control + * 256 aux data control messages + * 257 aux data packets + * */ + u_int16_t packet_len; +}; + +/* Open Continue Header */ +struct oc_header { + struct cu_header cu_head; + u_int16_t client_count; /* Number of client info structs */ + u_int32_t seq_no; + char user_name[20]; + char stuff[4]; /* Flags, version stuff, etc */ +}; + +/* Client info structures */ +struct client_info { + u_int32_t address; /* Client address */ + char stuff[8]; /* Flags, pruning bitfield, packet counts, etc */ +}; +#pragma pack() + +/* This structure is per expected connection */ +struct ip_ct_cuseeme_expect { +}; + +/* This structure exists only once per master */ +struct ip_ct_cuseeme_master { +}; + +#endif /* _IP_CT_CUSEEME */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_h323.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_h323.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_h323.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_h323.h Sun Apr 18 17:33:06 2004 @@ -0,0 +1,31 @@ +#ifndef _IP_CONNTRACK_H323_H +#define _IP_CONNTRACK_H323_H +/* H.323 connection tracking. */ + +#ifdef __KERNEL__ +/* Protects H.323 related data */ +#include +DECLARE_LOCK_EXTERN(ip_h323_lock); +#endif + +/* Default H.225 port */ +#define H225_PORT 1720 + +/* This structure is per expected connection */ +struct ip_ct_h225_expect { + u_int16_t port; /* Port of the H.225 helper/RTCP/RTP channel */ + enum ip_conntrack_dir dir; /* Direction of the original connection */ + unsigned int offset; /* offset of the address in the payload */ +}; + +/* This structure exists only once per master */ +struct ip_ct_h225_master { + int is_h225; /* H.225 or H.245 connection */ +#ifdef CONFIG_IP_NF_NAT_NEEDED + enum ip_conntrack_dir dir; /* Direction of the original connection */ + u_int32_t seq[IP_CT_DIR_MAX]; /* Exceptional packet mangling for signal addressess... */ + unsigned int offset[IP_CT_DIR_MAX]; /* ...and the offset of the addresses in the payload */ +#endif +}; + +#endif /* _IP_CONNTRACK_H323_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_mms.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_mms.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_mms.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_mms.h Sun Apr 18 17:33:07 2004 @@ -0,0 +1,31 @@ +#ifndef _IP_CONNTRACK_MMS_H +#define _IP_CONNTRACK_MMS_H +/* MMS tracking. */ + +#ifdef __KERNEL__ +#include + +DECLARE_LOCK_EXTERN(ip_mms_lock); + +#define MMS_PORT 1755 +#define MMS_SRV_MSG_ID 196610 + +#define MMS_SRV_MSG_OFFSET 36 +#define MMS_SRV_UNICODE_STRING_OFFSET 60 +#define MMS_SRV_CHUNKLENLV_OFFSET 16 +#define MMS_SRV_CHUNKLENLM_OFFSET 32 +#define MMS_SRV_MESSAGELENGTH_OFFSET 8 +#endif + +/* This structure is per expected connection */ +struct ip_ct_mms_expect { + u_int32_t len; + u_int32_t padding; + u_int16_t port; +}; + +/* This structure exists only once per master */ +struct ip_ct_mms_master { +}; + +#endif /* _IP_CONNTRACK_MMS_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_quake3.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_quake3.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_quake3.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_quake3.h Sun Apr 18 17:33:08 2004 @@ -0,0 +1,21 @@ +#ifndef _IP_CT_QUAKE3 +#define _IP_CT_QUAKE3 + +/* Don't confuse with 27960, often used as the Server Port */ +#define QUAKE3_MASTER_PORT 27950 + +struct quake3_search { + const char marker[4]; /* always 0xff 0xff 0xff 0xff ? */ + const char *pattern; + size_t plen; +}; + +/* This structure is per expected connection */ +struct ip_ct_quake3_expect { +}; + +/* This structure exists only once per master */ +struct ip_ct_quake3_master { +}; + +#endif /* _IP_CT_QUAKE3 */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_rpc.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_rpc.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_rpc.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_rpc.h Sun Apr 18 17:33:09 2004 @@ -0,0 +1,68 @@ +/* RPC extension for IP connection tracking, Version 2.2 + * (C) 2000 by Marcelo Barbosa Lima + * - original rpc tracking module + * - "recent" connection handling for kernel 2.3+ netfilter + * + * (C) 2001 by Rusty Russell + * - upgraded conntrack modules to oldnat api - kernel 2.4.0+ + * + * (C) 2002 by Ian (Larry) Latter + * - upgraded conntrack modules to newnat api - kernel 2.4.20+ + * - extended matching to support filtering on procedures + * + * ip_conntrack_rpc.h,v 2.2 2003/01/12 18:30:00 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + ** + */ + +#include +#include +#include +#include +#include + +#include + +#ifndef _IP_CONNTRACK_RPC_H +#define _IP_CONNTRACK_RPC_H + +#define RPC_PORT 111 + + +/* Datum in RPC packets are encoded in XDR */ +#define IXDR_GET_INT32(buf) ((u_int32_t) ntohl((uint32_t)*buf)) + +/* Fast timeout, to deny DoS atacks */ +#define EXP (60 * HZ) + +/* Normal timeouts */ +#define EXPIRES (180 * HZ) + +/* For future conections RPC, using client's cache bindings + * I'll use ip_conntrack_lock to lock these lists */ + +/* This identifies each request and stores protocol */ +struct request_p { + struct list_head list; + + u_int32_t xid; + u_int32_t ip; + u_int16_t port; + + /* Protocol */ + u_int16_t proto; + + struct timer_list timeout; +}; + +static inline int request_p_cmp(const struct request_p *p, u_int32_t xid, + u_int32_t ip, u_int32_t port) { + return (p->xid == xid && p->ip == ip && p->port); + +} + +#endif /* _IP_CONNTRACK_RPC_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_rsh.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_rsh.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_rsh.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_rsh.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,35 @@ +/* RSH extension for IP connection tracking, Version 1.0 + * (C) 2002 by Ian (Larry) Latter + * based on HW's ip_conntrack_irc.c + * + * ip_conntrack_rsh.c,v 1.0 2002/07/17 14:49:26 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _IP_CONNTRACK_RSH_H +#define _IP_CONNTRACK_RSH_H + +#ifdef __KERNEL__ +#include + +DECLARE_LOCK_EXTERN(ip_rsh_lock); +#endif + + +#define RSH_PORT 514 + +/* This structure is per expected connection */ +struct ip_ct_rsh_expect +{ + u_int16_t port; +}; + +/* This structure exists only once per master */ +struct ip_ct_rsh_master { +}; + +#endif /* _IP_CONNTRACK_RSH_H */ + diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_rtsp.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_rtsp.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_rtsp.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_rtsp.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,68 @@ +/* + * RTSP extension for IP connection tracking. + * (C) 2003 by Tom Marshall + * based on ip_conntrack_irc.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. + */ +#ifndef _IP_CONNTRACK_RTSP_H +#define _IP_CONNTRACK_RTSP_H + +/* #define IP_NF_RTSP_DEBUG */ +#define IP_NF_RTSP_VERSION "0.01" + +/* port block types */ +typedef enum { + pb_single, /* client_port=x */ + pb_range, /* client_port=x-y */ + pb_discon /* client_port=x/y (rtspbis) */ +} portblock_t; + +/* We record seq number and length of rtsp headers here, all in host order. */ + +/* + * This structure is per expected connection. It is a member of struct + * ip_conntrack_expect. The TCP SEQ for the conntrack expect is stored + * there and we are expected to only store the length of the data which + * needs replaced. If a packet contains multiple RTSP messages, we create + * one expected connection per message. + * + * We use these variables to mark the entire header block. This may seem + * like overkill, but the nature of RTSP requires it. A header may appear + * multiple times in a message. We must treat two Transport headers the + * same as one Transport header with two entries. + */ +struct ip_ct_rtsp_expect +{ + u_int32_t len; /* length of header block */ + portblock_t pbtype; /* Type of port block that was requested */ + u_int16_t loport; /* Port that was requested, low or first */ + u_int16_t hiport; /* Port that was requested, high or second */ +#if 0 + uint method; /* RTSP method */ + uint cseq; /* CSeq from request */ +#endif +}; + +/* This structure exists only once per master */ +struct ip_ct_rtsp_master +{ + /* Empty (?) */ +}; + + +#ifdef __KERNEL__ + +#include + +#define RTSP_PORT 554 + +/* Protects rtsp part of conntracks */ +DECLARE_LOCK_EXTERN(ip_rtsp_lock); + +#endif /* __KERNEL__ */ + +#endif /* _IP_CONNTRACK_RTSP_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_talk.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_talk.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_talk.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_talk.h Sun Apr 18 17:33:11 2004 @@ -0,0 +1,152 @@ +#ifndef _IP_CONNTRACK_TALK_H +#define _IP_CONNTRACK_TALK_H +/* TALK tracking. */ + +#ifdef __KERNEL__ +#include +#include + +/* Protects talk part of conntracks */ +DECLARE_LOCK_EXTERN(ip_talk_lock); +#endif + + +#define TALK_PORT 517 +#define NTALK_PORT 518 + +/* talk structures and constants from */ + +/* + * 4.3BSD struct sockaddr + */ +struct talk_addr { + u_int16_t ta_family; + u_int16_t ta_port; + u_int32_t ta_addr; + u_int32_t ta_junk1; + u_int32_t ta_junk2; +}; + +#define TALK_OLD_NSIZE 9 +#define TALK_NSIZE 12 +#define TALK_TTY_NSIZE 16 + +/* + * Client->server request message formats. + */ +struct talk_msg { + u_char type; /* request type, see below */ + char l_name[TALK_OLD_NSIZE];/* caller's name */ + char r_name[TALK_OLD_NSIZE];/* callee's name */ + u_char pad; + u_int32_t id_num; /* message id */ + int32_t pid; /* caller's process id */ + char r_tty[TALK_TTY_NSIZE];/* callee's tty name */ + struct talk_addr addr; /* old (4.3) style */ + struct talk_addr ctl_addr; /* old (4.3) style */ +}; + +struct ntalk_msg { + u_char vers; /* protocol version */ + u_char type; /* request type, see below */ + u_char answer; /* not used */ + u_char pad; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* old (4.3) style */ + struct talk_addr ctl_addr; /* old (4.3) style */ + int32_t pid; /* caller's process id */ + char l_name[TALK_NSIZE];/* caller's name */ + char r_name[TALK_NSIZE];/* callee's name */ + char r_tty[TALK_TTY_NSIZE];/* callee's tty name */ +}; + +struct ntalk2_msg { + u_char vers; /* talk protocol version */ + u_char type; /* request type */ + u_char answer; /* */ + u_char extended; /* !0 if additional parts */ + u_int32_t id_num; /* message id number (dels) */ + struct talk_addr addr; /* target address */ + struct talk_addr ctl_addr; /* reply to address */ + int32_t pid; /* caller's process id */ + char l_name[TALK_NSIZE]; /* caller's name */ + char r_name[TALK_NSIZE]; /* callee's name */ + char r_tty[TALK_TTY_NSIZE]; /* callee's tty */ +}; + +/* + * Server->client response message formats. + */ +struct talk_response { + u_char type; /* type of request message, see below */ + u_char answer; /* response to request message, see below */ + u_char pad[2]; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* address for establishing conversation */ +}; + +struct ntalk_response { + u_char vers; /* protocol version */ + u_char type; /* type of request message, see below */ + u_char answer; /* response to request message, see below */ + u_char pad; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* address for establishing conversation */ +}; + +struct ntalk2_response { + u_char vers; /* protocol version */ + u_char type; /* type of request message */ + u_char answer; /* response to request */ + u_char rvers; /* Version of answering vers*/ + u_int32_t id_num; /* message id number */ + struct talk_addr addr; /* address for connection */ + /* This is at the end to compatiblize this with NTALK version. */ + char r_name[TALK_NSIZE]; /* callee's name */ +}; + +#define TALK_STR(data, talk_str, member) ((struct talk_str *)data)->member) +#define TALK_RESP(data, ver, member) (ver ? ((struct ntalk_response *)data)->member : ((struct talk_response *)data)->member) +#define TALK_MSG(data, ver, member) (ver ? ((struct ntalk_msg *)data)->member : ((struct talk_msg *)data)->member) + +#define TALK_VERSION 0 /* protocol versions */ +#define NTALK_VERSION 1 +#define NTALK2_VERSION 2 + +/* message type values */ +#define LEAVE_INVITE 0 /* leave invitation with server */ +#define LOOK_UP 1 /* check for invitation by callee */ +#define DELETE 2 /* delete invitation by caller */ +#define ANNOUNCE 3 /* announce invitation by caller */ +/* NTALK2 */ +#define REPLY_QUERY 4 /* request reply data from local daemon */ + +/* answer values */ +#define SUCCESS 0 /* operation completed properly */ +#define NOT_HERE 1 /* callee not logged in */ +#define FAILED 2 /* operation failed for unexplained reason */ +#define MACHINE_UNKNOWN 3 /* caller's machine name unknown */ +#define PERMISSION_DENIED 4 /* callee's tty doesn't permit announce */ +#define UNKNOWN_REQUEST 5 /* request has invalid type value */ +#define BADVERSION 6 /* request has invalid protocol version */ +#define BADADDR 7 /* request has invalid addr value */ +#define BADCTLADDR 8 /* request has invalid ctl_addr value */ +/* NTALK2 */ +#define NO_CALLER 9 /* no-one calling answer from REPLY */ +#define TRY_HERE 10 /* Not on this machine, try this */ +#define SELECTIVE_REFUSAL 11 /* User Filter refusal. */ +#define MAX_RESPONSE_TYPE 11 /* Make sure this is updated */ + +/* We don't really need much for talk */ +struct ip_ct_talk_expect +{ + /* Port that was to be used */ + u_int16_t port; +}; + +/* This structure exists only once per master */ +struct ip_ct_talk_master +{ +}; + +#endif /* _IP_CONNTRACK_TALK_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_tcp.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_tcp.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_conntrack_tcp.h Tue Jan 7 15:50:46 2003 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_conntrack_tcp.h Sun Apr 18 17:33:11 2004 @@ -4,25 +4,41 @@ 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 + TCP_CONNTRACK_MAX, + TCP_CONNTRACK_IGNORE +}; + +/* Window scaling is advertised by the sender */ +#define IP_CT_TCP_STATE_FLAG_WINDOW_SCALE 0x01 + +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 */ + u_int8_t loose; /* used when connection picked up from the middle */ + u_int8_t flags; /* per direction state flags */ }; 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; + struct ip_ct_tcp_state seen[2]; /* connection parameters per direction */ + u_int8_t state; /* state of the connection (enum tcp_conntrack) */ + /* For detecting stale connections */ + u_int8_t last_dir; /* Direction of the last packet (enum ip_conntrack_dir) */ + u_int8_t retrans; /* Number of retransmitted packets */ + u_int8_t stored_seq; /* What is stored in last_seq */ + u_int32_t last_seq; /* Last sequence number seen in dir */ + u_int32_t last_end; /* Last seq + len */ }; #endif /* _IP_CONNTRACK_TCP_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_logging.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_logging.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_logging.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_logging.h Sun Apr 18 17:33:07 2004 @@ -0,0 +1,20 @@ +/* IPv4 macros for the internal logging interface. */ +#ifndef __IP_LOGGING_H +#define __IP_LOGGING_H + +#ifdef __KERNEL__ +#include +#include + +#define nf_log_ip_packet(pskb,hooknum,in,out,fmt,args...) \ + nf_log_packet(AF_INET,pskb,hooknum,in,out,fmt,##args) + +#define nf_log_ip(pfh,len,fmt,args...) \ + nf_log(AF_INET,pfh,len,fmt,##args) + +#define nf_ip_log_register(logging) nf_log_register(AF_INET,logging) +#define nf_ip_log_unregister(logging) nf_log_unregister(AF_INET,logging) + +#endif /*__KERNEL__*/ + +#endif /*__IP_LOGGING_H*/ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_pool.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_pool.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_pool.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_pool.h Sun Apr 18 17:33:08 2004 @@ -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 -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_set.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_set.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,440 @@ +#ifndef _IP_SET_H +#define _IP_SET_H + +/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu) + * Patrick Schaaf (bof@bof.de) + * Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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_SET 83 + +/* Directions: */ +#define IPSET_SRC 0x01 +#define IPSET_DST 0x02 +/* Inverse flag for matching: */ +#define IPSET_MATCH_INV 0x04 +/* Overwrite at adding a new entry: */ +#define IPSET_ADD_OVERWRITE 0x08 +/* Set typecodes: */ +#define IPSET_TYPE_IP 0x10 +#define IPSET_TYPE_PORT 0x20 + +/*FIXME: remove this */ +/* #define CONFIG_IP_NF_SET_DEBUG */ + +/* + * Heavily modify by Joakim Axelsson 08.03.2002 + * - Made it more modulebased + * + * Additional heavy modifications by Jozsef Kadlecsik 22.02.2004 + * - multilevel pools (sets) + * - in order to "deal with" backward compatibility, renamed to ipset + */ + +/* Used so that the kernel module and ipset-binary can match thier versions + */ +#define IP_SET_PROTOCOL_VERSION 1 + +#define IP_SET_MAXNAMELEN 32 /* set names and set typenames */ + +/* The max level of the sets. + * Do not increase lightheartedly before eliminating + * the recursive functions from ip_set.c. + */ +/* So many IPs can identify a set: */ +#define IP_SET_SETIP_LEVELS 4 +/* Max level of a set: */ +#define IP_SET_LEVELS (IP_SET_SETIP_LEVELS+1) + +/* Lets work with our own typedef for representing an IP address. + * We hope to make the code more portable, possibly to IPv6... + * + * The representation works in HOST byte order, because most set types + * will perform arithmetic operations and compare operations. + * + * For now the type is an uint32_t. + * + * We do not enforce, but assume that a set may not store more than + * 65536 entries. + * + * Make sure to ONLY use the functions when translating and parsing + * in order to keep the host byte order and make it more portable: + * parse_ip() + * parse_mask() + * parse_ipandmask() + * ip_tostring() + * (Joakim: where are they???) + */ + +typedef uint32_t ip_set_ip_t; + +/* SO_IP_SET operation constants, and their request struct types. + */ + +/* IP_SET_REQ_BASE defines the first components of ANY request structure. + * It is used for all SO_IP_SET calls, set or get. + */ +#define IP_SET_REQ_BASE \ + unsigned op; \ + int id \ + +struct ip_set_req_base { + IP_SET_REQ_BASE; +}; + +struct ip_set_req_std { + IP_SET_REQ_BASE; + ip_set_ip_t ip[IP_SET_SETIP_LEVELS]; + u_int8_t level; +}; + +#define IP_SET_OP_CREATE 0x00000001 /* Create a new (empty) set */ +struct ip_set_req_create { + IP_SET_REQ_BASE; + char name[IP_SET_MAXNAMELEN]; + char typename[IP_SET_LEVELS][IP_SET_MAXNAMELEN]; + u_int8_t levels; +}; + +#define IP_SET_OP_DESTROY 0x00000002 /* Remove a (empty) set */ +/* Uses ip_set_req_std */ + +#define IP_SET_OP_CREATE_CHILD 0x00000003 /* Create a new child set */ +struct ip_set_req_sub { + IP_SET_REQ_BASE; + ip_set_ip_t ip[IP_SET_SETIP_LEVELS]; + u_int8_t level; + u_int8_t childsets; +}; + +#define IP_SET_OP_FLUSH 0x00000004 /* Remove all IPs in a set */ +/* Uses ip_set_req_sub */ + +#define IP_SET_OP_RENAME 0x00000005 /* Rename a set */ +struct ip_set_req_rename { + IP_SET_REQ_BASE; + char newname[IP_SET_MAXNAMELEN]; +}; + +#define IP_SET_OP_SWAP 0x00000006 /* Swap two sets */ +struct ip_set_req_swap { + IP_SET_REQ_BASE; + int to; +}; + +#define IP_SET_OP_ADD_IP 0x00000007 /* Add an IP to a set */ +/* Uses ip_set_req_std, with type specific addage */ + +#define IP_SET_OP_DEL_IP 0x00000008 /* Remove an IP from a set */ +/* Uses ip_set_req_std, with type specific addage */ + +/* Test if an IP is in the set + */ +#define IP_SET_OP_TEST_IP 0x00000009 /* Test an IP in a set */ +struct ip_set_req_test { + IP_SET_REQ_BASE; + ip_set_ip_t ip[IP_SET_SETIP_LEVELS]; + u_int8_t level; + int reply; /* Test result */ +}; + +#define IP_SET_OP_VERSION 0x00000010 +struct ip_set_req_version { + IP_SET_REQ_BASE; + unsigned version; +}; + +/* List operations: + * Size requests are sent by ip_set_req_list + * except for LISTING. + */ +#define IP_SET_OP_LIST_HEADER_SIZE 0x00000101 +#define IP_SET_OP_LIST_HEADER 0x00000102 +#define IP_SET_OP_LIST_MEMBERS_SIZE 0x00000103 +#define IP_SET_OP_LIST_MEMBERS 0x00000104 +#define IP_SET_OP_LIST_CHILDSETS_SIZE 0x00000105 +#define IP_SET_OP_LIST_CHILDSETS 0x00000106 +struct ip_set_req_list { + IP_SET_REQ_BASE; + ip_set_ip_t ip[IP_SET_SETIP_LEVELS]; + u_int8_t level; + size_t size; +}; + +#define IP_SET_OP_LISTING_SIZE 0x00000107 +#define IP_SET_OP_LISTING 0x00000108 + +struct ip_set_req_listing_size { + IP_SET_REQ_BASE; + size_t size; +}; + +struct ip_set_req_listing { + char name[IP_SET_MAXNAMELEN]; + char typename[IP_SET_LEVELS][IP_SET_MAXNAMELEN]; + u_int8_t levels; + unsigned ref; + int id; +}; + +/* Between the iptables(8) set extension modules and the kernel we + * identify a set by its id. + * + * The GETSET_BYNAME call passes the name of a set to the kernel, and + * the a valid set id is returned if the set is still exist. + * The GETSET_BYID call passes the id a set to the kernel, and + * the set name is returned if the set is still exist. + */ +#define IP_SET_OP_GETSET_BYNAME 0x00000011 +struct ip_set_req_get { + IP_SET_REQ_BASE; + unsigned ref; + char name[IP_SET_MAXNAMELEN]; +}; + +#define IP_SET_OP_GETSET_BYID 0x00000012 +/* Uses ip_set_req_get */ + +static inline int bitmap_bytes(ip_set_ip_t a, ip_set_ip_t b) +{ + return 4 * ((((b - a + 8) / 8) + 3) / 4); +} + +#ifdef __KERNEL__ + +#define ip_set_printk(format, args...) \ + do { \ + printk("%s: %s: ", __FILE__, __FUNCTION__); \ + printk(format "\n" , ## args); \ + } while (0) + +#if defined(CONFIG_IP_NF_SET_DEBUG) || defined(CONFIG_IP_NF_SET_DEBUG_MODULE) +#define CONFIG_IP_NF_SET_DEBUG +#define CONFIG_IP_NF_SET_DEBUG_MODULE + +#define DP(format, args...) \ + do { \ + printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\ + printk(format "\n" , ## args); \ + } while (0) +#else +#define DP(format, args...) +#endif + +/* Generic set type: */ +struct ip_set_private { + struct ip_set_private **childsets; /* child sets */ + + /* type speficic members */ +}; + +/* + * The ip_set_type_t definition - one per set type, e.g. "ipmap". + * + * Each individual set has a pointer, set->type, going to one + * of these structures. Function pointers inside the structure implement + * the real behaviour of the sets. + * + * If not mentioned differently, the implementation behind the function + * pointers of a set_type, is expected to return 0 if ok, and a negative + * errno (e.g. -EINVAL) on error. + */ +struct ip_set_type { + struct list_head list; /* next in list of set types */ + + /* match IP in set - internally required + * return 0 if not in set, 1 if in set or + * negative errno if input was invalid + */ + int (*matchip) (struct ip_set_private *private, + ip_set_ip_t ip, + ip_set_ip_t *id); + + /* test for IP in set (kernel: iptables -m set --entry x) + * return 0 if not in set, 1 if in set. + */ + int (*testip_kernel) (struct ip_set_private *private, + const struct sk_buff * skb, + u_int32_t flags, + ip_set_ip_t *id); + + /* test for IP in set (userspace: ipset -T set --entry x) + * return 0 if not in set, 1 if in set. + */ + int (*testip) (struct ip_set_private *private, + const void *data, size_t size, + ip_set_ip_t *id); + + /* + * Size of the data structure passed by when + * adding/deletin/testing an entry. + */ + size_t reqsize; + + /* Add IP into set (userspace: ipset -A set --entry x) + * Return -EEXIST if the address is already in the set, + * and -ERANGE if the address lies outside the set bounds. + * If the address was not already in the set, 0 is returned. + */ + int (*addip) (struct ip_set_private *private, + const void *data, size_t size, + ip_set_ip_t *id); + + /* Add IP into set (kernel: iptables ... -j SET --entry x) + * Return -EEXIST if the address is already in the set, + * and -ERANGE if the address lies outside the set bounds. + * If the address was not already in the set, 0 is returned. + */ + int (*addip_kernel) (struct ip_set_private *private, + const struct sk_buff * skb, + u_int32_t flags, + ip_set_ip_t *id); + + /* remove IP from set (userspace: ipset -D set --entry x) + * Return -EEXIST if the address is NOT in the set, + * and -ERANGE if the address lies outside the set bounds. + * If the address really was in the set, 0 is returned. + */ + int (*delip) (struct ip_set_private *private, + const void *data, size_t size, + ip_set_ip_t *id); + + /* remove IP from set (kernel: iptables ... -j SET --entry x) + * Return -EEXIST if the address is NOT in the set, + * and -ERANGE if the address lies outside the set bounds. + * If the address really was in the set, 0 is returned. + */ + int (*delip_kernel) (struct ip_set_private *private, + const struct sk_buff * skb, + u_int32_t flags, + ip_set_ip_t *id); + + /* new set creation - allocated type specific items + */ + int (*create) (struct ip_set_private **private, + const void *data, size_t size); + + /* set destruction - free type specific items + * There is no return value. + * Can be called only when child sets are destroyed. + */ + void (*destroy) (struct ip_set_private **private); + + /* set flushing - reset all bits in the set, or something similar. + * There is no return value. + */ + void (*flush) (struct ip_set_private *private); + + /* Listing: Get size needed for header + */ + int (*list_header_size) (const struct ip_set_private *private); + + /* Listing: Get the header + * + * Fill in the information in "data". + * This function is always run after list_header_size() under a + * writelock on the set. Therefor is the length of "data" always + * correct. + */ + void (*list_header) (const struct ip_set_private *private, + void *data); + + /* Listing: Get the size for the set members + */ + int (*list_members_size) (const struct ip_set_private *private); + + /* Listing: Get the set members + * + * Fill in the information in "data". + * This function is always run after list_member_size() under a + * writelock on the set. Therefor is the length of "data" always + * correct. + */ + void (*list_members) (const struct ip_set_private *private, + void *data); + + /* Listing: set size in ids (first id is 0. Cannot change for a set). + */ + ip_set_ip_t (*sizeid) (const struct ip_set_private *private); + + /* Listing: Get the bitmap for the valid childsets + */ + void (*list_childsets) (const struct ip_set_private *private, + void *data); + + char typename[IP_SET_MAXNAMELEN]; + char typecode; + int protocol_version; + + /* Set this to THIS_MODULE if you are a module, otherwise NULL */ + struct module *me; +}; + +extern int ip_set_register_set_type(struct ip_set_type *set_type); +extern void ip_set_unregister_set_type(struct ip_set_type *set_type); + +/* A generic ipset */ +struct ip_set { + struct list_head list; /* next in list of all sets */ + rwlock_t lock; /* a lock for concurrency control */ + unsigned ref; /* reference counter */ + unsigned subref; /* reference counter at creating/destroying childsets */ + u_int8_t levels; /* max levels of subsets */ + struct ip_set_type *type[IP_SET_LEVELS]; /* the set types */ + struct ip_set_private *private; /* type specific data */ + char name[IP_SET_MAXNAMELEN]; /* the proper name of the set */ +}; + +extern struct ip_set **ip_set_list; + +/* register and unregister set pointer references */ +extern struct ip_set *ip_set_get_byname(const char name[IP_SET_MAXNAMELEN], + int *id); +extern struct ip_set *ip_set_get_byid(int id); +extern void ip_set_put(struct ip_set *set); + +/* API for iptables set match, and SET target */ +extern void ip_set_addip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level); +extern void ip_set_delip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level); +extern int ip_set_testip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level); + +#endif /* __KERNEL__ */ + +#endif /*_IP_SET_H*/ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_set_iphash.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set_iphash.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_set_iphash.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set_iphash.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,30 @@ +#ifndef __IP_SET_IPHASH_H +#define __IP_SET_IPHASH_H + +#include + +#define SETTYPE_NAME "iphash" +#define MAX_RANGE 0x0000FFFF + +struct ip_set_iphash { + struct ip_set_private **childsets; /* child sets */ + + /* Type speficic members: */ + uint32_t initval; /* initval for jhash_1word */ + ip_set_ip_t hashsize; /* hash size */ + ip_set_ip_t netmask; /* netmask */ + ip_set_ip_t *members; /* the iphash proper */ +}; + +struct ip_set_req_iphash_create { + uint32_t initval; + ip_set_ip_t hashsize; + ip_set_ip_t netmask; +}; + +struct ip_set_req_iphash { + ip_set_ip_t ip; + u_int32_t flags; +}; + +#endif /* __IP_SET_IPHASH_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_set_ipmap.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set_ipmap.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_set_ipmap.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set_ipmap.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,59 @@ +#ifndef __IP_SET_IPMAP_H +#define __IP_SET_IPMAP_H + +#include + +#define SETTYPE_NAME "ipmap" +#define MAX_RANGE 0x0000FFFF + +struct ip_set_ipmap { + struct ip_set_private **childsets; /* child sets */ + + /* Type speficic members: */ + ip_set_ip_t first_ip; /* host byte order, included in range */ + ip_set_ip_t last_ip; /* host byte order, included in range */ + ip_set_ip_t netmask; /* subnet netmask */ + ip_set_ip_t sizeid; /* size of set in ids */ + u_int16_t hosts; /* hosts per subnet */ + void *members; /* the ipmap proper */ +}; + +struct ip_set_req_ipmap_create { + ip_set_ip_t from; + ip_set_ip_t to; + ip_set_ip_t netmask; +}; + +struct ip_set_req_ipmap { + ip_set_ip_t ip; +}; + +unsigned int +mask_to_bits(ip_set_ip_t mask) +{ + unsigned int bits = 32; + ip_set_ip_t maskaddr; + + if (mask == 0xFFFFFFFF) + return bits; + + maskaddr = 0xFFFFFFFE; + while (--bits >= 0 && maskaddr != mask) + maskaddr <<= 1; + + return bits; +} + +ip_set_ip_t +range_to_mask(ip_set_ip_t from, ip_set_ip_t to, unsigned int *bits) +{ + ip_set_ip_t mask = 0xFFFFFFFE; + + *bits = 32; + while (--(*bits) >= 0 && mask && (to & mask) != from) + mask <<= 1; + + return mask; +} + +#endif /* __IP_SET_IPMAP_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_set_jhash.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set_jhash.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_set_jhash.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set_jhash.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,148 @@ +#ifndef _LINUX_IPSET_JHASH_H +#define _LINUX_IPSET_JHASH_H + +/* This is a copy of linux/jhash.h but the types u32/u8 are changed + * to __u32/__u8 so that the header file can be included into + * userspace code as well. Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + */ + +/* jhash.h: Jenkins hash support. + * + * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * http://burtleburtle.net/bob/hash/ + * + * These are the credits from Bob's sources: + * + * lookup2.c, by Bob Jenkins, December 1996, Public Domain. + * hash(), hash2(), hash3, and mix() are externally useful functions. + * Routines to test the hash are included if SELF_TEST is defined. + * You can use this free for any purpose. It has no warranty. + * + * Copyright (C) 2003 David S. Miller (davem@redhat.com) + * + * I've modified Bob's hash to be useful in the Linux kernel, and + * any bugs present are surely my fault. -DaveM + */ + +/* NOTE: Arguments are modified. */ +#define __jhash_mix(a, b, c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* The golden ration: an arbitrary value */ +#define JHASH_GOLDEN_RATIO 0x9e3779b9 + +/* The most generic version, hashes an arbitrary sequence + * of bytes. No alignment or length assumptions are made about + * the input key. + */ +static inline __u32 jhash(void *key, __u32 length, __u32 initval) +{ + __u32 a, b, c, len; + __u8 *k = key; + + len = length; + a = b = JHASH_GOLDEN_RATIO; + c = initval; + + while (len >= 12) { + a += (k[0] +((__u32)k[1]<<8) +((__u32)k[2]<<16) +((__u32)k[3]<<24)); + b += (k[4] +((__u32)k[5]<<8) +((__u32)k[6]<<16) +((__u32)k[7]<<24)); + c += (k[8] +((__u32)k[9]<<8) +((__u32)k[10]<<16)+((__u32)k[11]<<24)); + + __jhash_mix(a,b,c); + + k += 12; + len -= 12; + } + + c += length; + switch (len) { + case 11: c += ((__u32)k[10]<<24); + case 10: c += ((__u32)k[9]<<16); + case 9 : c += ((__u32)k[8]<<8); + case 8 : b += ((__u32)k[7]<<24); + case 7 : b += ((__u32)k[6]<<16); + case 6 : b += ((__u32)k[5]<<8); + case 5 : b += k[4]; + case 4 : a += ((__u32)k[3]<<24); + case 3 : a += ((__u32)k[2]<<16); + case 2 : a += ((__u32)k[1]<<8); + case 1 : a += k[0]; + }; + + __jhash_mix(a,b,c); + + return c; +} + +/* A special optimized version that handles 1 or more of __u32s. + * The length parameter here is the number of __u32s in the key. + */ +static inline __u32 jhash2(__u32 *k, __u32 length, __u32 initval) +{ + __u32 a, b, c, len; + + a = b = JHASH_GOLDEN_RATIO; + c = initval; + len = length; + + while (len >= 3) { + a += k[0]; + b += k[1]; + c += k[2]; + __jhash_mix(a, b, c); + k += 3; len -= 3; + } + + c += length * 4; + + switch (len) { + case 2 : b += k[1]; + case 1 : a += k[0]; + }; + + __jhash_mix(a,b,c); + + return c; +} + + +/* A special ultra-optimized versions that knows they are hashing exactly + * 3, 2 or 1 word(s). + * + * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally + * done at the end is not done here. + */ +static inline __u32 jhash_3words(__u32 a, __u32 b, __u32 c, __u32 initval) +{ + a += JHASH_GOLDEN_RATIO; + b += JHASH_GOLDEN_RATIO; + c += initval; + + __jhash_mix(a, b, c); + + return c; +} + +static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) +{ + return jhash_3words(a, b, 0, initval); +} + +static inline __u32 jhash_1word(__u32 a, __u32 initval) +{ + return jhash_3words(a, 0, 0, initval); +} + +#endif /* _LINUX_IPSET_JHASH_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_set_macipmap.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set_macipmap.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_set_macipmap.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set_macipmap.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,41 @@ +#ifndef __IP_SET_MACIPMAP_H +#define __IP_SET_MACIPMAP_H + +#include + +#define SETTYPE_NAME "macipmap" +#define MAX_RANGE 0x0000FFFF + +/* general flags */ +#define IPSET_MACIP_MATCHUNSET 1 + +/* per ip flags */ +#define IPSET_MACIP_ISSET 1 + +struct ip_set_macipmap { + struct ip_set_private **childsets; /* child sets */ + + /* Type speficic members: */ + ip_set_ip_t first_ip; /* host byte order, included in range */ + ip_set_ip_t last_ip; /* host byte order, included in range */ + void *members; /* the macipmap proper */ + u_int32_t flags; +}; + +struct ip_set_req_macipmap_create { + ip_set_ip_t from; + ip_set_ip_t to; + u_int32_t flags; +}; + +struct ip_set_req_macipmap { + ip_set_ip_t ip; + unsigned char ethernet[ETH_ALEN]; +}; + +struct ip_set_macip { + unsigned short flags; + unsigned char ethernet[ETH_ALEN]; +}; + +#endif /* __IP_SET_MACIPMAP_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ip_set_portmap.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set_portmap.h --- linux-2.4.26/include/linux/netfilter_ipv4/ip_set_portmap.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ip_set_portmap.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,28 @@ +#ifndef __IP_SET_PORTMAP_H +#define __IP_SET_PORTMAP_H + +#include + +#define SETTYPE_NAME "portmap" +#define MAX_RANGE 0x0000FFFF +#define INVALID_PORT (MAX_RANGE + 1) + +struct ip_set_portmap { + struct ip_set_private **childsets; /* child sets */ + + /* Type speficic members: */ + ip_set_ip_t first_port; /* host byte order, included in range */ + ip_set_ip_t last_port; /* host byte order, included in range */ + void *members; /* the portmap proper */ +}; + +struct ip_set_req_portmap_create { + ip_set_ip_t from; + ip_set_ip_t to; +}; + +struct ip_set_req_portmap { + ip_set_ip_t port; +}; + +#endif /* __IP_SET_PORTMAP_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_CLASSIFY.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_CLASSIFY.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_CLASSIFY.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_CLASSIFY.h Sun Apr 18 17:33:01 2004 @@ -0,0 +1,8 @@ +#ifndef _IPT_CLASSIFY_H +#define _IPT_CLASSIFY_H + +struct ipt_classify_target_info { + u_int32_t priority; +}; + +#endif /*_IPT_CLASSIFY_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_NETLINK.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_NETLINK.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_NETLINK.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_NETLINK.h Sun Apr 18 17:33:02 2004 @@ -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 -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_ROUTE.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_ROUTE.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_ROUTE.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_ROUTE.h Sun Apr 18 17:33:03 2004 @@ -0,0 +1,22 @@ +/* Header file for iptables ipt_ROUTE target + * + * (C) 2002 by Cédric de Launois + * + * This software is distributed under GNU GPL v2, 1991 + */ +#ifndef _IPT_ROUTE_H_target +#define _IPT_ROUTE_H_target + +#define IPT_ROUTE_IFNAMSIZ 16 + +struct ipt_route_target_info { + char oif[IPT_ROUTE_IFNAMSIZ]; /* Output Interface Name */ + char iif[IPT_ROUTE_IFNAMSIZ]; /* Input Interface Name */ + u_int32_t gw; /* IP address of gateway */ + u_int8_t flags; +}; + +/* Values for "flags" field */ +#define IPT_ROUTE_CONTINUE 0x01 + +#endif /*_IPT_ROUTE_H_target*/ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_SAME.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_SAME.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_SAME.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_SAME.h Sun Apr 18 17:33:04 2004 @@ -0,0 +1,19 @@ +#ifndef _IPT_SAME_H +#define _IPT_SAME_H + +#define IPT_SAME_MAX_RANGE 10 + +#define IPT_SAME_NODST 0x01 + +struct ipt_same_info +{ + unsigned char info; + u_int32_t rangesize; + u_int32_t ipnum; + u_int32_t *iparray; + + /* hangs off end. */ + struct ip_nat_range range[IPT_SAME_MAX_RANGE]; +}; + +#endif /*_IPT_SAME_H*/ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_TTL.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_TTL.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_TTL.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_TTL.h Sun Apr 18 17:33:04 2004 @@ -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 -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_ULOG.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_ULOG.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_ULOG.h Mon Jan 6 17:42:16 2003 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_ULOG.h Sun Apr 18 17:33:07 2004 @@ -11,6 +11,9 @@ #define NETLINK_NFLOG 5 #endif +#define NFLOG_DEFAULT_NLGROUP 1 +#define NFLOG_DEFAULT_QTHRESHOLD 1 + #define ULOG_MAC_LEN 80 #define ULOG_PREFIX_LEN 32 diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_XOR.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_XOR.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_XOR.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_XOR.h Sun Apr 18 17:33:04 2004 @@ -0,0 +1,9 @@ +#ifndef _IPT_XOR_H +#define _IPT_XOR_H + +struct ipt_XOR_info { + char key[30]; + u_int8_t block_size; +}; + +#endif /* _IPT_XOR_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_account.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_account.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_account.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_account.h Sun Apr 18 17:33:04 2004 @@ -0,0 +1,21 @@ +/* + * accounting match (ipt_account.c) + * (C) 2003,2004 by Piotr Gasid³o (quaker@barbara.eu.org) + * + * Version: 0.1.5 + * + * This software is distributed under the terms of GNU GPL + */ + +#ifndef _IPT_ACCOUNT_H_ +#define _IPT_ACCOUNT_H_ + +#define IPT_ACCOUNT_NAME_LEN 64 + +struct t_ipt_account_info { + char name[IPT_ACCOUNT_NAME_LEN]; + u_int32_t network; + u_int32_t netmask; +}; + +#endif diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_addrtype.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_addrtype.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_addrtype.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_addrtype.h Sun Apr 18 17:33:05 2004 @@ -0,0 +1,11 @@ +#ifndef _IPT_ADDRTYPE_H +#define _IPT_ADDRTYPE_H + +struct ipt_addrtype_info { + u_int16_t source; /* source-type mask */ + u_int16_t dest; /* dest-type mask */ + int invert_source; + int invert_dest; +}; + +#endif diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_condition.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_condition.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_condition.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_condition.h Sun Apr 18 17:33:05 2004 @@ -0,0 +1,11 @@ +#ifndef __IPT_CONDITION_MATCH__ +#define __IPT_CONDITION_MATCH__ + +#define CONDITION_NAME_LEN 32 + +struct condition_info { + char name[CONDITION_NAME_LEN]; + int invert; +}; + +#endif diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_connlimit.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_connlimit.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_connlimit.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_connlimit.h Sun Apr 18 17:33:05 2004 @@ -0,0 +1,12 @@ +#ifndef _IPT_CONNLIMIT_H +#define _IPT_CONNLIMIT_H + +struct ipt_connlimit_data; + +struct ipt_connlimit_info { + int limit; + int inverse; + u_int32_t mask; + struct ipt_connlimit_data *data; +}; +#endif /* _IPT_CONNLIMIT_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_iprange.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_iprange.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_iprange.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_iprange.h Sun Apr 18 17:33:06 2004 @@ -0,0 +1,23 @@ +#ifndef _IPT_IPRANGE_H +#define _IPT_IPRANGE_H + +#define IPRANGE_SRC 0x01 /* Match source IP address */ +#define IPRANGE_DST 0x02 /* Match destination IP address */ +#define IPRANGE_SRC_INV 0x10 /* Negate the condition */ +#define IPRANGE_DST_INV 0x20 /* Negate the condition */ + +struct ipt_iprange { + /* Inclusive: network order. */ + u_int32_t min_ip, max_ip; +}; + +struct ipt_iprange_info +{ + struct ipt_iprange src; + struct ipt_iprange dst; + + /* Flags from above */ + u_int8_t flags; +}; + +#endif /* _IPT_IPRANGE_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_ipv4options.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_ipv4options.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_ipv4options.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_ipv4options.h Sun Apr 18 17:33:06 2004 @@ -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 -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_mport.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_mport.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_mport.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_mport.h Sun Apr 18 17:33:07 2004 @@ -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 -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_nth.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_nth.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_nth.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_nth.h Sun Apr 18 17:33:07 2004 @@ -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 -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_osf.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_osf.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_osf.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_osf.h Sun Apr 18 17:33:08 2004 @@ -0,0 +1,148 @@ +/* + * ipt_osf.h + * + * Copyright (c) 2003 Evgeniy Polyakov + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _IPT_OSF_H +#define _IPT_OSF_H + +#define MAXGENRELEN 32 +#define MAXDETLEN 64 + +#define IPT_OSF_GENRE 1 +#define IPT_OSF_SMART 2 +#define IPT_OSF_LOG 4 +#define IPT_OSF_NETLINK 8 + +#define IPT_OSF_LOGLEVEL_ALL 0 +#define IPT_OSF_LOGLEVEL_FIRST 1 + +#include + +#ifndef __KERNEL__ +#include +#include + +struct list_head +{ + struct list_head *prev, *next; +}; +#endif + +struct ipt_osf_info +{ + char genre[MAXGENRELEN]; + int len; + unsigned long flags; + int loglevel; + int invert; /* UNSUPPORTED */ +}; + +struct osf_wc +{ + char wc; + unsigned long val; +}; + +/* This struct represents IANA options + * http://www.iana.org/assignments/tcp-parameters + */ +struct osf_opt +{ + unsigned char kind; + unsigned char length; + struct osf_wc wc; +}; + +struct osf_finger +{ + struct list_head flist; + struct osf_wc wss; + unsigned char ttl; + unsigned char df; + unsigned long ss; + unsigned char genre[MAXGENRELEN]; + unsigned char version[MAXGENRELEN], subtype[MAXGENRELEN]; + + /* Not needed, but for consistency with original table from Michal Zalewski */ + unsigned char details[MAXDETLEN]; + + int opt_num; + struct osf_opt opt[MAX_IPOPTLEN]; /* In case it is all NOP or EOL */ + +}; + +struct ipt_osf_nlmsg +{ + struct osf_finger f; + struct iphdr ip; + struct tcphdr tcp; +}; + +#ifdef __KERNEL__ + +/* Defines for IANA option kinds */ + +#define OSFOPT_EOL 0 /* End of options */ +#define OSFOPT_NOP 1 /* NOP */ +#define OSFOPT_MSS 2 /* Maximum segment size */ +#define OSFOPT_WSO 3 /* Window scale option */ +#define OSFOPT_SACKP 4 /* SACK permitted */ +#define OSFOPT_SACK 5 /* SACK */ +#define OSFOPT_ECHO 6 +#define OSFOPT_ECHOREPLY 7 +#define OSFOPT_TS 8 /* Timestamp option */ +#define OSFOPT_POCP 9 /* Partial Order Connection Permitted */ +#define OSFOPT_POSP 10 /* Partial Order Service Profile */ +/* Others are not used in current OSF */ + +static struct osf_opt IANA_opts[] = +{ + {0, 1,}, + {1, 1,}, + {2, 4,}, + {3, 3,}, + {4, 2,}, + {5, 1 ,}, /* SACK length is not defined */ + {6, 6,}, + {7, 6,}, + {8, 10,}, + {9, 2,}, + {10, 3,}, + {11, 1,}, /* CC: Suppose 1 */ + {12, 1,}, /* the same */ + {13, 1,}, /* and here too */ + {14, 3,}, + {15, 1,}, /* TCP Alternate Checksum Data. Length is not defined */ + {16, 1,}, + {17, 1,}, + {18, 3,}, + {19, 18,}, + {20, 1,}, + {21, 1,}, + {22, 1,}, + {23, 1,}, + {24, 1,}, + {25, 1,}, + {26, 1,}, +}; + +#endif /* __KERNEL__ */ + +#endif /* _IPT_OSF_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_pool.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_pool.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_pool.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_pool.h Sun Apr 18 17:33:08 2004 @@ -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 -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_psd.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_psd.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_psd.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_psd.h Sun Apr 18 17:33:08 2004 @@ -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 -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_random.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_random.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_random.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_random.h Sun Apr 18 17:33:09 2004 @@ -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 -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_rpc.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_rpc.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_rpc.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_rpc.h Sun Apr 18 17:33:09 2004 @@ -0,0 +1,35 @@ +/* RPC extension for IP netfilter matching, Version 2.2 + * (C) 2000 by Marcelo Barbosa Lima + * - original rpc tracking module + * - "recent" connection handling for kernel 2.3+ netfilter + * + * (C) 2001 by Rusty Russell + * - upgraded conntrack modules to oldnat api - kernel 2.4.0+ + * + * (C) 2002 by Ian (Larry) Latter + * - upgraded conntrack modules to newnat api - kernel 2.4.20+ + * - extended matching to support filtering on procedures + * + * ipt_rpc.h.c,v 2.2 2003/01/12 18:30:00 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + ** + */ + +#ifndef _IPT_RPC_H +#define _IPT_RPC_H + +struct ipt_rpc_data; + +struct ipt_rpc_info { + int inverse; + int strict; + const char c_procs[1408]; + int i_procs; + struct ipt_rpc_data *data; +}; + +#endif /* _IPT_RPC_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_set.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_set.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_set.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_set.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,22 @@ +#ifndef _IPT_SET_H +#define _IPT_SET_H + +#include + +struct ipt_set_info { + int16_t id; + u_int8_t set_level, ip_level; + u_int32_t flags[IP_SET_LEVELS]; +}; + +/* match info */ +struct ipt_set_info_match { + struct ipt_set_info match; +}; + +struct ipt_set_info_target { + struct ipt_set_info add_set; + struct ipt_set_info del_set; +}; + +#endif /*_IPT_SET_H*/ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_string.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_string.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_string.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_string.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,21 @@ +#ifndef _IPT_STRING_H +#define _IPT_STRING_H + +/* *** PERFORMANCE TWEAK *** + * Packet size and search string threshold, + * above which sublinear searches is used. */ +#define IPT_STRING_HAYSTACK_THRESH 100 +#define IPT_STRING_NEEDLE_THRESH 20 + +#define BM_MAX_NLEN 256 +#define BM_MAX_HLEN 1024 + +typedef char *(*proc_ipt_search) (char *, char *, int, int); + +struct ipt_string_info { + char string[BM_MAX_NLEN]; + u_int16_t invert; + u_int16_t len; +}; + +#endif /* _IPT_STRING_H */ diff -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_time.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_time.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_time.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_time.h Sun Apr 18 17:33:11 2004 @@ -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 -urN linux-2.4.26/include/linux/netfilter_ipv4/ipt_u32.h linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_u32.h --- linux-2.4.26/include/linux/netfilter_ipv4/ipt_u32.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv4/ipt_u32.h Sun Apr 18 17:33:11 2004 @@ -0,0 +1,40 @@ +#ifndef _IPT_U32_H +#define _IPT_U32_H +#include + +enum ipt_u32_ops +{ + IPT_U32_AND, + IPT_U32_LEFTSH, + IPT_U32_RIGHTSH, + IPT_U32_AT +}; + +struct ipt_u32_location_element +{ + u_int32_t number; + u_int8_t nextop; +}; +struct ipt_u32_value_element +{ + u_int32_t min; + u_int32_t max; +}; +/* *** any way to allow for an arbitrary number of elements? + for now I settle for a limit of 10 of each */ +#define U32MAXSIZE 10 +struct ipt_u32_test +{ + u_int8_t nnums; + struct ipt_u32_location_element location[U32MAXSIZE+1]; + u_int8_t nvalues; + struct ipt_u32_value_element value[U32MAXSIZE+1]; +}; + +struct ipt_u32 +{ + u_int8_t ntests; + struct ipt_u32_test tests[U32MAXSIZE+1]; +}; + +#endif /*_IPT_U32_H*/ diff -urN linux-2.4.26/include/linux/netfilter_ipv6/ip6_logging.h linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6_logging.h --- linux-2.4.26/include/linux/netfilter_ipv6/ip6_logging.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6_logging.h Sun Apr 18 17:33:07 2004 @@ -0,0 +1,20 @@ +/* IPv6 macros for the nternal logging interface. */ +#ifndef __IP6_LOGGING_H +#define __IP6_LOGGING_H + +#ifdef __KERNEL__ +#include +#include + +#define nf_log_ip6_packet(pskb,hooknum,in,out,fmt,args...) \ + nf_log_packet(AF_INET6,pskb,hooknum,in,out,fmt,##args) + +#define nf_log_ip6(pfh,len,fmt,args...) \ + nf_log(AF_INET6,pfh,len,fmt,##args) + +#define nf_ip6_log_register(logging) nf_log_register(AF_INET6,logging) +#define nf_ip6_log_unregister(logging) nf_log_unregister(AF_INET6,logging) + +#endif /*__KERNEL__*/ + +#endif /*__IP6_LOGGING_H*/ diff -urN linux-2.4.26/include/linux/netfilter_ipv6/ip6t_REJECT.h linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_REJECT.h --- linux-2.4.26/include/linux/netfilter_ipv6/ip6t_REJECT.h Tue Jun 20 23:32:27 2000 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_REJECT.h Sun Apr 18 17:33:03 2004 @@ -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 -urN linux-2.4.26/include/linux/netfilter_ipv6/ip6t_ROUTE.h linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_ROUTE.h --- linux-2.4.26/include/linux/netfilter_ipv6/ip6t_ROUTE.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_ROUTE.h Sun Apr 18 17:33:03 2004 @@ -0,0 +1,22 @@ +/* Header file for iptables ip6t_ROUTE target + * + * (C) 2003 by Cédric de Launois + * + * This software is distributed under GNU GPL v2, 1991 + */ +#ifndef _IPT_ROUTE_H_target +#define _IPT_ROUTE_H_target + +#define IP6T_ROUTE_IFNAMSIZ 16 + +struct ip6t_route_target_info { + char oif[IP6T_ROUTE_IFNAMSIZ]; /* Output Interface Name */ + char iif[IP6T_ROUTE_IFNAMSIZ]; /* Input Interface Name */ + u_int32_t gw[4]; /* IPv6 address of gateway */ + u_int8_t flags; +}; + +/* Values for "flags" field */ +#define IP6T_ROUTE_CONTINUE 0x01 + +#endif /*_IP6T_ROUTE_H_target*/ diff -urN linux-2.4.26/include/linux/netfilter_ipv6/ip6t_condition.h linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_condition.h --- linux-2.4.26/include/linux/netfilter_ipv6/ip6t_condition.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_condition.h Sun Apr 18 17:33:05 2004 @@ -0,0 +1,11 @@ +#ifndef __IP6T_CONDITION_MATCH__ +#define __IP6T_CONDITION_MATCH__ + +#define CONDITION6_NAME_LEN 32 + +struct condition6_info { + char name[CONDITION6_NAME_LEN]; + int invert; +}; + +#endif diff -urN linux-2.4.26/include/linux/netfilter_ipv6/ip6t_nth.h linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_nth.h --- linux-2.4.26/include/linux/netfilter_ipv6/ip6t_nth.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_nth.h Sun Apr 18 17:33:07 2004 @@ -0,0 +1,19 @@ +#ifndef _IP6T_NTH_H +#define _IP6T_NTH_H + +#include +#include + +#ifndef IP6T_NTH_NUM_COUNTERS +#define IP6T_NTH_NUM_COUNTERS 16 +#endif + +struct ip6t_nth_info { + u_int8_t every; + u_int8_t not; + u_int8_t startat; + u_int8_t counter; + u_int8_t packet; +}; + +#endif /*_IP6T_NTH_H*/ diff -urN linux-2.4.26/include/linux/netfilter_ipv6/ip6t_owner.h linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_owner.h --- linux-2.4.26/include/linux/netfilter_ipv6/ip6t_owner.h Tue Jun 20 23:32:27 2000 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_owner.h Sun Apr 18 17:33:08 2004 @@ -6,12 +6,14 @@ #define IP6T_OWNER_GID 0x02 #define IP6T_OWNER_PID 0x04 #define IP6T_OWNER_SID 0x08 +#define IP6T_OWNER_COMM 0x10 struct ip6t_owner_info { uid_t uid; gid_t gid; pid_t pid; pid_t sid; + char comm[16]; u_int8_t match, invert; /* flags */ }; diff -urN linux-2.4.26/include/linux/netfilter_ipv6/ip6t_random.h linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_random.h --- linux-2.4.26/include/linux/netfilter_ipv6/ip6t_random.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_ipv6/ip6t_random.h Sun Apr 18 17:33:09 2004 @@ -0,0 +1,11 @@ +#ifndef _IP6T_RAND_H +#define _IP6T_RAND_H + +#include +#include + +struct ip6t_rand_info { + u_int8_t average; +}; + +#endif /*_IP6T_RAND_H*/ diff -urN linux-2.4.26/include/linux/netfilter_logging.h linux-2.4.26-pomng/include/linux/netfilter_logging.h --- linux-2.4.26/include/linux/netfilter_logging.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_logging.h Sun Apr 18 17:33:07 2004 @@ -0,0 +1,33 @@ +/* Internal logging interface, which relies on the real + LOG target modules */ +#ifndef __LINUX_NETFILTER_LOGGING_H +#define __LINUX_NETFILTER_LOGGING_H + +#ifdef __KERNEL__ +#include + +struct nf_logging_t { + void (*nf_log_packet)(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const char *prefix); + void (*nf_log)(char *pfh, size_t len, + const char *prefix); +}; + +extern void nf_log_register(int pf, const struct nf_logging_t *logging); +extern void nf_log_unregister(int pf, const struct nf_logging_t *logging); + +extern void nf_log_packet(int pf, + struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const char *fmt, ...); +extern void nf_log(int pf, + char *pfh, size_t len, + const char *fmt, ...); +#endif /*__KERNEL__*/ + +#endif /*__LINUX_NETFILTER_LOGGING_H*/ diff -urN linux-2.4.26/include/linux/netfilter_mime.h linux-2.4.26-pomng/include/linux/netfilter_mime.h --- linux-2.4.26/include/linux/netfilter_mime.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/include/linux/netfilter_mime.h Sun Apr 18 17:33:10 2004 @@ -0,0 +1,89 @@ +/* + * MIME functions for netfilter modules. This file provides implementations + * for basic MIME parsing. MIME headers are used in many protocols, such as + * HTTP, RTSP, SIP, etc. + * + * gcc will warn for defined but unused functions, so we only include the + * functions requested. The following macros are used: + * NF_NEED_MIME_NEXTLINE nf_mime_nextline() + */ +#ifndef _NETFILTER_MIME_H +#define _NETFILTER_MIME_H + +/* Only include these functions for kernel code. */ +#ifdef __KERNEL__ + +#include + +/* + * Given a buffer and length, advance to the next line and mark the current + * line. If the current line is empty, *plinelen will be set to zero. If + * not, it will be set to the actual line length (including CRLF). + * + * 'line' in this context means logical line (includes LWS continuations). + * Returns 1 on success, 0 on failure. + */ +#ifdef NF_NEED_MIME_NEXTLINE +static int +nf_mime_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen) +{ + uint off = *poff; + uint physlen = 0; + int is_first_line = 1; + + if (off >= len) + { + return 0; + } + + do + { + while (p[off] != '\n') + { + if (len-off <= 1) + { + return 0; + } + + physlen++; + off++; + } + + /* if we saw a crlf, physlen needs adjusted */ + if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r') + { + physlen--; + } + + /* advance past the newline */ + off++; + + /* check for an empty line */ + if (physlen == 0) + { + break; + } + + /* check for colon on the first physical line */ + if (is_first_line) + { + is_first_line = 0; + if (memchr(p+(*poff), ':', physlen) == NULL) + { + return 0; + } + } + } + while (p[off] == ' ' || p[off] == '\t'); + + *plineoff = *poff; + *plinelen = (physlen == 0) ? 0 : (off - *poff); + *poff = off; + + return 1; +} +#endif /* NF_NEED_MIME_NEXTLINE */ + +#endif /* __KERNEL__ */ + +#endif /* _NETFILTER_MIME_H */ diff -urN linux-2.4.26/include/linux/sysctl.h linux-2.4.26-pomng/include/linux/sysctl.h --- linux-2.4.26/include/linux/sysctl.h Sat Apr 17 23:05:49 2004 +++ linux-2.4.26-pomng/include/linux/sysctl.h Sun Apr 18 17:33:11 2004 @@ -383,6 +383,11 @@ NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT=12, NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT=13, NET_IPV4_NF_CONNTRACK_BUCKETS=14, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_MAX_RETRANS=15, + NET_IPV4_NF_CONNTRACK_TCP_LOG_INVALID=16, + NET_IPV4_NF_CONNTRACK_TCP_LOOSE=17, + NET_IPV4_NF_CONNTRACK_TCP_BE_LIBERAL=18, + NET_IPV4_NF_CONNTRACK_TCP_MAX_RETRANS=19, }; /* /proc/sys/net/ipv6 */ diff -urN linux-2.4.26/include/net/tcp.h linux-2.4.26-pomng/include/net/tcp.h --- linux-2.4.26/include/net/tcp.h Sat Apr 17 23:05:49 2004 +++ linux-2.4.26-pomng/include/net/tcp.h Sun Apr 18 17:33:08 2004 @@ -141,6 +141,7 @@ extern void tcp_bucket_unlock(struct sock *sk); extern int tcp_port_rover; extern struct sock *tcp_v4_lookup_listener(u32 addr, unsigned short hnum, int dif); +extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 hnum, int dif); /* These are AF independent. */ static __inline__ int tcp_bhashfn(__u16 lport) diff -urN linux-2.4.26/include/net/udp.h linux-2.4.26-pomng/include/net/udp.h --- linux-2.4.26/include/net/udp.h Wed Mar 31 20:25:01 2004 +++ linux-2.4.26-pomng/include/net/udp.h Sun Apr 18 17:33:08 2004 @@ -69,6 +69,8 @@ extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); extern int udp_disconnect(struct sock *sk, int flags); +extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif); + extern struct udp_mib udp_statistics[NR_CPUS*2]; #define UDP_INC_STATS(field) SNMP_INC_STATS(udp_statistics, field) #define UDP_INC_STATS_BH(field) SNMP_INC_STATS_BH(udp_statistics, field) diff -urN linux-2.4.26/net/core/netfilter.c linux-2.4.26-pomng/net/core/netfilter.c --- linux-2.4.26/net/core/netfilter.c Wed Jul 30 09:19:18 2003 +++ linux-2.4.26-pomng/net/core/netfilter.c Sun Apr 18 17:33:07 2004 @@ -8,9 +8,12 @@ * * February 2000: Modified by James Morris to have 1 queue per protocol. * 15-Mar-2000: Added NF_REPEAT --RR. + * 08-May-2003: Internal logging interface added by Jozsef Kadlecsik. */ #include +#include #include +#include #include #include #include @@ -621,6 +624,75 @@ return 0; } #endif /*CONFIG_INET*/ + +/* Internal logging interface, which relies on the real + LOG target modules */ + +#define NF_LOG_PREFIXLEN 128 + +static struct nf_logging_t nf_logging[NPROTO] = {}; +static int reported = 0; + +void nf_log_register(int pf, const struct nf_logging_t *logging) +{ + br_write_lock_bh(BR_NETPROTO_LOCK); + if (!nf_logging[pf].nf_log_packet) { + nf_logging[pf].nf_log_packet = logging->nf_log_packet; + nf_logging[pf].nf_log = logging->nf_log; + } + br_write_unlock_bh(BR_NETPROTO_LOCK); +} + +void nf_log_unregister(int pf, const struct nf_logging_t *logging) +{ + br_write_lock_bh(BR_NETPROTO_LOCK); + if (nf_logging[pf].nf_log_packet == logging->nf_log_packet) { + nf_logging[pf].nf_log_packet = NULL; + nf_logging[pf].nf_log = NULL; + } + br_write_unlock_bh(BR_NETPROTO_LOCK); +} + +void nf_log_packet(int pf, + struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const char *fmt, ...) +{ + va_list args; + char prefix[NF_LOG_PREFIXLEN]; + + if (nf_logging[pf].nf_log_packet) { + va_start(args, fmt); + vsnprintf(prefix, sizeof(prefix), fmt, args); + va_end(args); + nf_logging[pf].nf_log_packet(pskb, hooknum, in, out, prefix); + } else if (!reported) { + printk(KERN_WARNING "nf_log_packet: can\'t log yet, " + "no backend logging module loaded in!\n"); + reported++; + } +} + +void nf_log(int pf, + char *pfh, size_t len, + const char *fmt, ...) +{ + va_list args; + char prefix[NF_LOG_PREFIXLEN]; + + if (nf_logging[pf].nf_log) { + va_start(args, fmt); + vsnprintf(prefix, sizeof(prefix), fmt, args); + va_end(args); + nf_logging[pf].nf_log(pfh, len, prefix); + } else if (!reported) { + printk(KERN_WARNING "nf_log: can\'t log yet, " + "no backend logging module loaded in!\n"); + reported++; + } +} /* This does not belong here, but ipt_REJECT needs it if connection tracking in use: without this, connection may not be in hash table, diff -urN linux-2.4.26/net/ipv4/netfilter/Config.in linux-2.4.26-pomng/net/ipv4/netfilter/Config.in --- linux-2.4.26/net/ipv4/netfilter/Config.in Wed Jul 30 09:19:06 2003 +++ linux-2.4.26-pomng/net/ipv4/netfilter/Config.in Sun Apr 18 17:33:11 2004 @@ -7,9 +7,17 @@ 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 ' RSH protocol support' CONFIG_IP_NF_RSH $CONFIG_IP_NF_CONNTRACK + dep_tristate ' H.323 (netmeeting) support' CONFIG_IP_NF_H323 $CONFIG_IP_NF_CONNTRACK + dep_tristate ' Eggdrop bot support' CONFIG_IP_NF_EGG $CONFIG_IP_NF_CONNTRACK dep_tristate ' Amanda protocol support' CONFIG_IP_NF_AMANDA $CONFIG_IP_NF_CONNTRACK dep_tristate ' TFTP protocol support' CONFIG_IP_NF_TFTP $CONFIG_IP_NF_CONNTRACK dep_tristate ' IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK + dep_tristate ' RTSP protocol support' CONFIG_IP_NF_RTSP $CONFIG_IP_NF_CONNTRACK + dep_tristate ' Quake III protocol support' CONFIG_IP_NF_QUAKE3 $CONFIG_IP_NF_CONNTRACK + dep_tristate ' MMS protocol support' CONFIG_IP_NF_MMS $CONFIG_IP_NF_CONNTRACK + dep_tristate ' CuSeeMe protocol support' CONFIG_IP_NF_CUSEEME $CONFIG_IP_NF_CONNTRACK fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then @@ -18,12 +26,42 @@ tristate 'IP tables support (required for filtering/masq/NAT)' CONFIG_IP_NF_IPTABLES if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; then # The simple matches. + if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then + dep_tristate ' RPC match support' CONFIG_IP_NF_MATCH_RPC $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES + fi dep_tristate ' limit match support' CONFIG_IP_NF_MATCH_LIMIT $CONFIG_IP_NF_IPTABLES + + dep_tristate ' IP address/port sets support' CONFIG_IP_NF_SET $CONFIG_IP_NF_IPTABLES + if [ "$CONFIG_IP_NF_SET" != "n" ]; then + int ' Maximum number of sets' CONFIG_IP_NF_SET_MAX 256 + dep_tristate ' ipmap set type support' CONFIG_IP_NF_SET_IPMAP $CONFIG_IP_NF_SET + dep_tristate ' portmap set type support' CONFIG_IP_NF_SET_PORTMAP $CONFIG_IP_NF_SET + dep_tristate ' macipmap set type support' CONFIG_IP_NF_SET_MACIPMAP $CONFIG_IP_NF_SET + dep_tristate ' iphash set type support' CONFIG_IP_NF_SET_IPHASH $CONFIG_IP_NF_SET + fi + + dep_tristate ' IP address pool support' CONFIG_IP_NF_POOL $CONFIG_IP_NF_IPTABLES + if [ "$CONFIG_IP_NF_POOL" = "y" -o "$CONFIG_IP_NF_POOL" = "m" ]; then + bool ' enable statistics on pool usage' CONFIG_IP_POOL_STATISTICS n + fi + + dep_tristate ' IP range match support' CONFIG_IP_NF_MATCH_IPRANGE $CONFIG_IP_NF_IPTABLES dep_tristate ' MAC address match support' CONFIG_IP_NF_MATCH_MAC $CONFIG_IP_NF_IPTABLES dep_tristate ' Packet type match support' CONFIG_IP_NF_MATCH_PKTTYPE $CONFIG_IP_NF_IPTABLES dep_tristate ' netfilter MARK match support' CONFIG_IP_NF_MATCH_MARK $CONFIG_IP_NF_IPTABLES dep_tristate ' Multiple port match support' CONFIG_IP_NF_MATCH_MULTIPORT $CONFIG_IP_NF_IPTABLES + dep_tristate ' Multiple port with ranges match support' CONFIG_IP_NF_MATCH_MPORT $CONFIG_IP_NF_IPTABLES dep_tristate ' TOS match support' CONFIG_IP_NF_MATCH_TOS $CONFIG_IP_NF_IPTABLES + dep_tristate ' TIME match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_TIME $CONFIG_IP_NF_IPTABLES + dep_tristate ' random match support' CONFIG_IP_NF_MATCH_RANDOM $CONFIG_IP_NF_IPTABLES + dep_tristate ' psd match support' CONFIG_IP_NF_MATCH_PSD $CONFIG_IP_NF_IPTABLES + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' OSF match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OSF $CONFIG_IP_NF_IPTABLES + fi + dep_tristate ' Nth match support' CONFIG_IP_NF_MATCH_NTH $CONFIG_IP_NF_IPTABLES + dep_tristate ' IPV4OPTIONS match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_IPV4OPTIONS $CONFIG_IP_NF_IPTABLES + dep_tristate ' condition match support' CONFIG_IP_NF_MATCH_CONDITION $CONFIG_IP_NF_IPTABLES + dep_tristate ' account match support' CONFIG_IP_NF_MATCH_ACCOUNT $CONFIG_IP_NF_IPTABLES dep_tristate ' recent match support' CONFIG_IP_NF_MATCH_RECENT $CONFIG_IP_NF_IPTABLES dep_tristate ' ECN match support' CONFIG_IP_NF_MATCH_ECN $CONFIG_IP_NF_IPTABLES @@ -31,25 +69,32 @@ dep_tristate ' AH/ESP match support' CONFIG_IP_NF_MATCH_AH_ESP $CONFIG_IP_NF_IPTABLES dep_tristate ' LENGTH match support' CONFIG_IP_NF_MATCH_LENGTH $CONFIG_IP_NF_IPTABLES + dep_tristate ' U32 match support' CONFIG_IP_NF_MATCH_U32 $CONFIG_IP_NF_U32 dep_tristate ' TTL match support' CONFIG_IP_NF_MATCH_TTL $CONFIG_IP_NF_IPTABLES + dep_tristate ' address type match support' CONFIG_IP_NF_MATCH_ADDRTYPE $CONFIG_IP_NF_IPTABLES dep_tristate ' tcpmss match support' CONFIG_IP_NF_MATCH_TCPMSS $CONFIG_IP_NF_IPTABLES if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then dep_tristate ' Helper match support' CONFIG_IP_NF_MATCH_HELPER $CONFIG_IP_NF_IPTABLES fi if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then dep_tristate ' Connection state match support' CONFIG_IP_NF_MATCH_STATE $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES + dep_tristate ' Connections/IP limit match support' CONFIG_IP_NF_MATCH_CONNLIMIT $CONFIG_IP_NF_IPTABLES dep_tristate ' Connection tracking match support' CONFIG_IP_NF_MATCH_CONNTRACK $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES + dep_tristate ' String match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_STRING $CONFIG_IP_NF_IPTABLES dep_tristate ' Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES fi # The targets dep_tristate ' Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES if [ "$CONFIG_IP_NF_FILTER" != "n" ]; then dep_tristate ' REJECT target support' CONFIG_IP_NF_TARGET_REJECT $CONFIG_IP_NF_FILTER +dep_tristate ' NETLINK target support' CONFIG_IP_NF_TARGET_NETLINK $CONFIG_IP_NF_FILTER + dep_tristate ' IPV4OPTSSTRIP target support' CONFIG_IP_NF_TARGET_IPV4OPTSSTRIP $CONFIG_IP_NF_FILTER if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' MIRROR target support (EXPERIMENTAL)' CONFIG_IP_NF_TARGET_MIRROR $CONFIG_IP_NF_FILTER + dep_tristate ' TARPIT target support (EXPERIMENTAL)' CONFIG_IP_NF_TARGET_TARPIT $CONFIG_IP_NF_FILTER fi fi @@ -59,6 +104,24 @@ define_bool CONFIG_IP_NF_NAT_NEEDED y dep_tristate ' MASQUERADE target support' CONFIG_IP_NF_TARGET_MASQUERADE $CONFIG_IP_NF_NAT dep_tristate ' REDIRECT target support' CONFIG_IP_NF_TARGET_REDIRECT $CONFIG_IP_NF_NAT + # If they want talk, set to $CONFIG_IP_NF_NAT (m or y), + # or $CONFIG_IP_NF_TALK (m or y), whichever is weaker. Argh. + if [ "$CONFIG_IP_NF_TALK" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_TALK m + else + if [ "$CONFIG_IP_NF_TALK" = "y" ]; then + define_tristate CONFIG_IP_NF_NAT_TALK $CONFIG_IP_NF_NAT + fi + fi + if [ "$CONFIG_IP_NF_H323" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_H323 m + else + if [ "$CONFIG_IP_NF_H323" = "y" ]; then + define_tristate CONFIG_IP_NF_NAT_H323 $CONFIG_IP_NF_NAT + fi + fi + dep_tristate ' SAME target support' CONFIG_IP_NF_TARGET_SAME $CONFIG_IP_NF_NAT + dep_tristate ' NETMAP target support' CONFIG_IP_NF_TARGET_NETMAP $CONFIG_IP_NF_NAT if [ "$CONFIG_IP_NF_AMANDA" = "m" ]; then define_tristate CONFIG_IP_NF_NAT_AMANDA m else @@ -69,6 +132,13 @@ 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 + if [ "$CONFIG_IP_NF_RTSP" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_RTSP m + else + if [ "$CONFIG_IP_NF_RTSP" = "y" ]; then + define_tristate CONFIG_IP_NF_NAT_RTSP $CONFIG_IP_NF_NAT + fi + fi fi if [ "$CONFIG_IP_NF_IRC" = "m" ]; then define_tristate CONFIG_IP_NF_NAT_IRC m @@ -77,6 +147,27 @@ define_tristate CONFIG_IP_NF_NAT_IRC $CONFIG_IP_NF_NAT fi fi + if [ "$CONFIG_IP_NF_QUAKE3" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_QUAKE3 m + else + if [ "$CONFIG_IP_NF_QUAKE3" = "y" ]; then + define_tristate CONFIG_IP_NF_NAT_QUAKE3 $CONFIG_IP_NF_NAT + fi + fi + if [ "$CONFIG_IP_NF_MMS" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_MMS m + else + if [ "$CONFIG_IP_NF_MMS" = "y" ]; then + define_tristate CONFIG_IP_NF_NAT_MMS $CONFIG_IP_NF_NAT + fi + fi + if [ "$CONFIG_IP_NF_CUSEEME" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_CUSEEME m + else + if [ "$CONFIG_IP_NF_CUSEEME" = "y" ]; then + define_tristate CONFIG_IP_NF_NAT_CUSEEME $CONFIG_IP_NF_NAT + fi + fi # If they want FTP, set to $CONFIG_IP_NF_NAT (m or y), # or $CONFIG_IP_NF_FTP (m or y), whichever is weaker. Argh. if [ "$CONFIG_IP_NF_FTP" = "m" ]; then @@ -104,8 +195,12 @@ dep_tristate ' DSCP target support' CONFIG_IP_NF_TARGET_DSCP $CONFIG_IP_NF_MANGLE dep_tristate ' MARK target support' CONFIG_IP_NF_TARGET_MARK $CONFIG_IP_NF_MANGLE + dep_tristate ' ROUTE target support' CONFIG_IP_NF_TARGET_ROUTE $CONFIG_IP_NF_MANGLE + dep_tristate ' CLASSIFY target support (EXPERIMENTAL)' CONFIG_IP_NF_TARGET_CLASSIFY $CONFIG_IP_NF_MANGLE fi dep_tristate ' LOG target support' CONFIG_IP_NF_TARGET_LOG $CONFIG_IP_NF_IPTABLES + dep_tristate ' XOR target support' CONFIG_IP_NF_TARGET_XOR $CONFIG_IP_NF_IPTABLES + dep_tristate ' 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 -urN linux-2.4.26/net/ipv4/netfilter/Makefile linux-2.4.26-pomng/net/ipv4/netfilter/Makefile --- linux-2.4.26/net/ipv4/netfilter/Makefile Wed Jul 30 09:19:06 2003 +++ linux-2.4.26-pomng/net/ipv4/netfilter/Makefile Sun Apr 18 17:33:11 2004 @@ -31,7 +31,44 @@ # connection tracking obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o +ifdef CONFIG_IP_NF_NAT_NEEDED + export-objs += ip_conntrack_proto_tcp.o +endif + + +# talk protocol support +obj-$(CONFIG_IP_NF_TALK) += ip_conntrack_talk.o +ifdef CONFIG_IP_NF_TALK + export-objs += ip_conntrack_talk.o +endif +obj-$(CONFIG_IP_NF_NAT_TALK) += ip_nat_talk.o + + +# H.323 support +obj-$(CONFIG_IP_NF_H323) += ip_conntrack_h323.o +ifdef CONFIG_IP_NF_H323 + export-objs += ip_conntrack_h323.o +endif +obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o + + # connection tracking helpers + +# rtsp protocol support +obj-$(CONFIG_IP_NF_RTSP) += ip_conntrack_rtsp.o +ifdef CONFIG_IP_NF_NAT_RTSP + export-objs += ip_conntrack_rtsp.o +endif +obj-$(CONFIG_IP_NF_NAT_RTSP) += ip_nat_rtsp.o + +obj-$(CONFIG_IP_NF_QUAKE3) += ip_conntrack_quake3.o +ifdef CONFIG_IP_NF_NAT_QUAKE3 + export-objs += ip_conntrack_quake3.o +endif +obj-$(CONFIG_IP_NF_MMS) += ip_conntrack_mms.o +ifdef CONFIG_IP_NF_MMS + export-objs += ip_conntrack_mms.o +endif obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o ifdef CONFIG_IP_NF_AMANDA export-objs += ip_conntrack_amanda.o @@ -39,6 +76,10 @@ obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o +obj-$(CONFIG_IP_NF_RSH) += ip_conntrack_rsh.o + +obj-$(CONFIG_IP_NF_EGG) += ip_conntrack_egg.o + ifdef CONFIG_IP_NF_FTP export-objs += ip_conntrack_ftp.o endif @@ -49,10 +90,13 @@ endif # NAT helpers +obj-$(CONFIG_IP_NF_NAT_CUSEEME) += ip_nat_cuseeme.o obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o +obj-$(CONFIG_IP_NF_NAT_QUAKE3) += ip_nat_quake3.o +obj-$(CONFIG_IP_NF_NAT_MMS) += ip_nat_mms.o # generic IP tables obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o @@ -63,16 +107,49 @@ obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o # matches +obj-$(CONFIG_IP_NF_MATCH_RPC) += ip_conntrack_rpc_tcp.o ip_conntrack_rpc_udp.o ipt_rpc.o +export-objs += ip_conntrack_rpc_tcp.o ip_conntrack_rpc_udp.o + obj-$(CONFIG_IP_NF_MATCH_HELPER) += ipt_helper.o obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o +obj-$(CONFIG_IP_NF_MATCH_IPRANGE) += ipt_iprange.o obj-$(CONFIG_IP_NF_MATCH_MARK) += ipt_mark.o +obj-$(CONFIG_IP_NF_SET) += ipt_set.o ipt_SET.o ip_set.o +ifdef CONFIG_IP_NF_SET + export-objs += ip_set.o +endif +obj-$(CONFIG_IP_NF_SET_IPMAP) += ip_set_ipmap.o +obj-$(CONFIG_IP_NF_SET_PORTMAP) += ip_set_portmap.o +obj-$(CONFIG_IP_NF_SET_MACIPMAP) += ip_set_macipmap.o +obj-$(CONFIG_IP_NF_SET_IPHASH) += ip_set_iphash.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_TIME) += ipt_time.o + + +obj-$(CONFIG_IP_NF_MATCH_RANDOM) += ipt_random.o + +obj-$(CONFIG_IP_NF_MATCH_PSD) += ipt_psd.o + +obj-$(CONFIG_IP_NF_MATCH_OSF) += ipt_osf.o + + +obj-$(CONFIG_IP_NF_MATCH_NTH) += ipt_nth.o + +obj-$(CONFIG_IP_NF_MATCH_IPV4OPTIONS) += ipt_ipv4options.o + +obj-$(CONFIG_IP_NF_MATCH_CONDITION) += ipt_condition.o +obj-$(CONFIG_IP_NF_MATCH_ACCOUNT) += ipt_account.o + obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o @@ -81,23 +158,38 @@ obj-$(CONFIG_IP_NF_MATCH_LENGTH) += ipt_length.o +obj-$(CONFIG_IP_NF_MATCH_U32) += ipt_u32.o + + obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o +obj-$(CONFIG_IP_NF_MATCH_CONNLIMIT) += ipt_connlimit.o obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o +obj-$(CONFIG_IP_NF_MATCH_STRING) += ipt_string.o obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o +obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o # targets obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o +obj-$(CONFIG_IP_NF_TARGET_CLASSIFY) += ipt_CLASSIFY.o obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o +obj-$(CONFIG_IP_NF_TARGET_TARPIT) += ipt_TARPIT.o obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o obj-$(CONFIG_IP_NF_TARGET_DSCP) += ipt_DSCP.o obj-$(CONFIG_IP_NF_TARGET_MARK) += ipt_MARK.o obj-$(CONFIG_IP_NF_TARGET_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_ROUTE) += ipt_ROUTE.o +obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o +obj-$(CONFIG_IP_NF_TARGET_XOR) += ipt_XOR.o +obj-$(CONFIG_IP_NF_TARGET_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 diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_core.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_core.c Fri Jan 16 11:26:27 2004 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_core.c Sun Apr 18 17:33:11 2004 @@ -33,8 +33,6 @@ /* For ERR_PTR(). Yeah, I know... --RR */ #include -/* This rwlock protects the main hash table, protocol/helper/expected - registrations, conntrack timers*/ #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock) #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock) @@ -44,7 +42,7 @@ #include #include -#define IP_CONNTRACK_VERSION "2.1" +#define IP_CONNTRACK_VERSION "2.2" #if 0 #define DEBUGP printk @@ -52,8 +50,11 @@ #define DEBUGP(format, args...) #endif +/* This rwlock protects the protocol/helper registrations only. */ DECLARE_RWLOCK(ip_conntrack_lock); -DECLARE_RWLOCK(ip_conntrack_expect_tuple_lock); +/* This lock protects the global and local expectation lists + and the expectation related data in the conntrack entries */ +DECLARE_RWLOCK(ip_conntrack_expect_lock); void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL; LIST_HEAD(ip_conntrack_expect_list); @@ -62,7 +63,7 @@ unsigned int ip_conntrack_htable_size = 0; int ip_conntrack_max = 0; static atomic_t ip_conntrack_count = ATOMIC_INIT(0); -struct list_head *ip_conntrack_hash; +struct ip_conntrack_hash *ip_conntrack_hash; static kmem_cache_t *ip_conntrack_cachep; extern struct ip_conntrack_protocol ip_conntrack_generic_protocol; @@ -73,25 +74,15 @@ return protocol == curr->proto; } -struct ip_conntrack_protocol *__ip_ct_find_proto(u_int8_t protocol) +struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol) { struct ip_conntrack_protocol *p; - MUST_BE_READ_LOCKED(&ip_conntrack_lock); + READ_LOCK(&ip_conntrack_lock); p = LIST_FIND(&protocol_list, proto_cmpfn, struct ip_conntrack_protocol *, protocol); if (!p) p = &ip_conntrack_generic_protocol; - - return p; -} - -struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol) -{ - struct ip_conntrack_protocol *p; - - READ_LOCK(&ip_conntrack_lock); - p = __ip_ct_find_proto(protocol); READ_UNLOCK(&ip_conntrack_lock); return p; } @@ -115,10 +106,35 @@ #if 0 dump_tuple(tuple); #endif +#ifdef CONFIG_IP_NF_NAT_NEEDED return (jhash_3words(tuple->src.ip, (tuple->dst.ip ^ tuple->dst.protonum), (tuple->src.u.all | (tuple->dst.u.all << 16)), ip_conntrack_hash_rnd) % ip_conntrack_htable_size); +#else + /* Non-NAT symmetry optizimation. */ + if (tuple->src.ip < tuple->dst.ip) { + /* Fine, just call jhash_3words */ + return (jhash_3words(tuple->src.ip, + (tuple->dst.ip ^ tuple->dst.protonum), + (tuple->src.u.all | (tuple->dst.u.all << 16)), + ip_conntrack_hash_rnd) % ip_conntrack_htable_size); + } else if (tuple->src.ip > tuple->dst.ip + || tuple->src.u.all > tuple->dst.u.all) { + /* Swap *both* addresses and ports */ + return (jhash_3words(tuple->dst.ip, + (tuple->src.ip ^ tuple->dst.protonum), + (tuple->dst.u.all | (tuple->src.u.all << 16)), + ip_conntrack_hash_rnd) % ip_conntrack_htable_size); + } else { + /* tuple->src.ip == tuple->dst.ip + && tuple->src.u.all <= tuple->dst.u.all */ + return (jhash_3words(tuple->src.ip, + (tuple->dst.ip ^ tuple->dst.protonum), + (tuple->src.u.all | (tuple->dst.u.all << 16)), + ip_conntrack_hash_rnd) % ip_conntrack_htable_size); + } +#endif } inline int @@ -167,7 +183,7 @@ static inline int expect_cmp(const struct ip_conntrack_expect *i, const struct ip_conntrack_tuple *tuple) { - MUST_BE_READ_LOCKED(&ip_conntrack_expect_tuple_lock); + MUST_BE_READ_LOCKED(&ip_conntrack_expect_lock); return ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask); } @@ -177,11 +193,14 @@ DEBUGP("destroy_expect(%p) use=%d\n", exp, atomic_read(&exp->use)); IP_NF_ASSERT(atomic_read(&exp->use)); IP_NF_ASSERT(!timer_pending(&exp->timeout)); + MUST_BE_READ_WRITE_UNLOCKED(&ip_conntrack_expect_lock); + + /* Release refcnt we hold on our creator */ + ip_conntrack_put(exp->expectant); kfree(exp); } - inline void ip_conntrack_expect_put(struct ip_conntrack_expect *exp) { IP_NF_ASSERT(exp); @@ -195,8 +214,7 @@ static inline struct ip_conntrack_expect * __ip_ct_expect_find(const struct ip_conntrack_tuple *tuple) { - MUST_BE_READ_LOCKED(&ip_conntrack_lock); - MUST_BE_READ_LOCKED(&ip_conntrack_expect_tuple_lock); + MUST_BE_READ_LOCKED(&ip_conntrack_expect_lock); return LIST_FIND(&ip_conntrack_expect_list, expect_cmp, struct ip_conntrack_expect *, tuple); } @@ -207,34 +225,38 @@ { struct ip_conntrack_expect *exp; - READ_LOCK(&ip_conntrack_lock); - READ_LOCK(&ip_conntrack_expect_tuple_lock); + READ_LOCK(&ip_conntrack_expect_lock); exp = __ip_ct_expect_find(tuple); if (exp) atomic_inc(&exp->use); - READ_UNLOCK(&ip_conntrack_expect_tuple_lock); - READ_UNLOCK(&ip_conntrack_lock); + READ_UNLOCK(&ip_conntrack_expect_lock); return exp; } +static inline void del_expect_from_lists(struct ip_conntrack_expect *expect) +{ + MUST_BE_WRITE_LOCKED(&ip_conntrack_expect_lock); + /* delete from global and local lists */ + list_del(&expect->list); + list_del(&expect->expected_list); + + /* decrement expect-count of master conntrack */ + expect->expectant->expecting--; +} + /* remove one specific expectation from all lists and drop refcount, - * does _NOT_ delete the timer. */ -static void __unexpect_related(struct ip_conntrack_expect *expect) + * May be called only when the timer is deleted or off. */ +static inline void __unexpect_related(struct ip_conntrack_expect *expect) { DEBUGP("unexpect_related(%p)\n", expect); - MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); /* we're not allowed to unexpect a confirmed expectation! */ IP_NF_ASSERT(!expect->sibling); - /* delete from global and local lists */ - list_del(&expect->list); - list_del(&expect->expected_list); - - /* decrement expect-count of master conntrack */ - if (expect->expectant) - expect->expectant->expecting--; + WRITE_LOCK(&ip_conntrack_expect_lock); + del_expect_from_lists(expect); + WRITE_UNLOCK(&ip_conntrack_expect_lock); ip_conntrack_expect_put(expect); } @@ -242,79 +264,106 @@ /* remove one specific expecatation from all lists, drop refcount * and expire timer. * This function can _NOT_ be called for confirmed expects! */ -static void unexpect_related(struct ip_conntrack_expect *expect) +static inline void unexpect_related(struct ip_conntrack_expect *expect) { IP_NF_ASSERT(expect->expectant); IP_NF_ASSERT(expect->expectant->helper); - /* if we are supposed to have a timer, but we can't delete - * it: race condition. __unexpect_related will - * be calledd by timeout function */ - if (expect->expectant->helper->timeout - && !del_timer(&expect->timeout)) - return; - __unexpect_related(expect); + /* Might already be dying. */ + if (del_timer(&expect->timeout)) + __unexpect_related(expect); } /* delete all unconfirmed expectations for this conntrack */ -static void remove_expectations(struct ip_conntrack *ct, int drop_refcount) +static void remove_expectations(struct ip_conntrack *ct) { - struct list_head *exp_entry, *next; + struct list_head *exp_entry, *tmp; struct ip_conntrack_expect *exp; + LIST_HEAD(unconfirmed); DEBUGP("remove_expectations(%p)\n", ct); - list_for_each_safe(exp_entry, next, &ct->sibling_list) { + WRITE_LOCK(&ip_conntrack_expect_lock); + list_for_each_safe(exp_entry, tmp, &ct->sibling_list) { exp = list_entry(exp_entry, struct ip_conntrack_expect, expected_list); + DEBUGP("remove_expectations: found %p of %p, use=%d\n", + exp, ct, atomic_read(&exp->use)); /* we skip established expectations, as we want to delete * the un-established ones only */ if (exp->sibling) { - DEBUGP("remove_expectations: skipping established %p of %p\n", exp->sibling, ct); - if (drop_refcount) { - /* Indicate that this expectations parent is dead */ - ip_conntrack_put(exp->expectant); - exp->expectant = NULL; - } + DEBUGP("remove_expectations: skipping established %p of %p\n", + exp->sibling, ct); continue; } IP_NF_ASSERT(list_inlist(&ip_conntrack_expect_list, exp)); IP_NF_ASSERT(exp->expectant == ct); - /* delete expectation from global and private lists */ - unexpect_related(exp); + /* Might already be dying... */ + if (del_timer(&exp->timeout)) { + del_expect_from_lists(exp); + list_add(&exp->expected_list, &unconfirmed); + } + } + WRITE_UNLOCK(&ip_conntrack_expect_lock); + + /* Now destroy them all, without the need to hold the lock. */ + while (!list_empty(&unconfirmed)) { + exp = list_entry(unconfirmed.next, struct ip_conntrack_expect, + expected_list); + list_del(unconfirmed.next); + + ip_conntrack_expect_put(exp); } } static void clean_from_lists(struct ip_conntrack *ct) { - unsigned int ho, hr; - DEBUGP("clean_from_lists(%p)\n", ct); - MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); - ho = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - hr = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); - LIST_DELETE(&ip_conntrack_hash[ho], &ct->tuplehash[IP_CT_DIR_ORIGINAL]); - LIST_DELETE(&ip_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]); - - /* Destroy all un-established, pending expectations */ - remove_expectations(ct, 1); + LIST_DELETE(&ip_conntrack_hash[ct->key[IP_CT_DIR_ORIGINAL]].list, + &ct->tuplehash[IP_CT_DIR_ORIGINAL]); +#ifdef CONFIG_IP_NF_NAT_NEEDED + LIST_DELETE(&ip_conntrack_hash[ct->key[IP_CT_DIR_REPLY]].list, + &ct->tuplehash[IP_CT_DIR_REPLY]); +#endif } static void destroy_conntrack(struct nf_conntrack *nfct) { - struct ip_conntrack *ct = (struct ip_conntrack *)nfct, *master = NULL; + 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)); + /* Destroy all un-established, pending expectations */ + remove_expectations(ct); + + /* If we were expected, delete our master expectation */ + if (ct->master) { + DEBUGP("destroy_conntrack(%p), expected by %p\n", + ct, ct->master); + IP_NF_ASSERT(ct->master->sibling); + /* Can't call __unexpect_related here, + * since it would screw up the global list */ + WRITE_LOCK(&ip_conntrack_expect_lock); + list_del(&ct->master->expected_list); + WRITE_UNLOCK(&ip_conntrack_expect_lock); + + /* release our refcount */ + ip_conntrack_expect_put(ct->master); + + /* delete our master expectation */ + IP_NF_ASSERT(atomic_read(&ct->master->use) == 1); + ip_conntrack_expect_put(ct->master); + } + /* 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 */ @@ -325,25 +374,6 @@ if (ip_conntrack_destroyed) ip_conntrack_destroyed(ct); - WRITE_LOCK(&ip_conntrack_lock); - /* Delete us from our own list to prevent corruption later */ - list_del(&ct->sibling_list); - - /* Delete our master expectation */ - if (ct->master) { - if (ct->master->expectant) { - /* can't call __unexpect_related here, - * since it would screw up expect_list */ - list_del(&ct->master->expected_list); - master = ct->master->expectant; - } - kfree(ct->master); - } - WRITE_UNLOCK(&ip_conntrack_lock); - - if (master) - ip_conntrack_put(master); - DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct); kmem_cache_free(ip_conntrack_cachep, ct); atomic_dec(&ip_conntrack_count); @@ -353,35 +383,35 @@ { struct ip_conntrack *ct = (void *)ul_conntrack; - WRITE_LOCK(&ip_conntrack_lock); + write_lock_ct(ct); clean_from_lists(ct); - WRITE_UNLOCK(&ip_conntrack_lock); - ip_conntrack_put(ct); -} + write_unlock_ct(ct); -static inline int -conntrack_tuple_cmp(const struct ip_conntrack_tuple_hash *i, - const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack) -{ - MUST_BE_READ_LOCKED(&ip_conntrack_lock); - return i->ctrack != ignored_conntrack - && ip_ct_tuple_equal(tuple, &i->tuple); + ip_conntrack_put(ct); } static struct ip_conntrack_tuple_hash * -__ip_conntrack_find(const struct ip_conntrack_tuple *tuple, +__ip_conntrack_find(u_int32_t key, + const struct ip_conntrack_tuple *tuple, const struct ip_conntrack *ignored_conntrack) { struct ip_conntrack_tuple_hash *h; - unsigned int hash = hash_conntrack(tuple); - MUST_BE_READ_LOCKED(&ip_conntrack_lock); - h = LIST_FIND(&ip_conntrack_hash[hash], - conntrack_tuple_cmp, - struct ip_conntrack_tuple_hash *, - tuple, ignored_conntrack); - return h; + MUST_BE_READ_LOCKED(&ip_conntrack_hash[key].lock); + list_for_each_entry(h, &ip_conntrack_hash[key].list, list) { + if (h->ctrack == ignored_conntrack) + continue; +#ifdef CONFIG_IP_NF_NAT_NEEDED + if (ip_ct_tuple_equal(tuple, &h->tuple)) + return h; +#else + if (ip_ct_tuple_equal(tuple, &h->ctrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple)) + return &h->ctrack->tuplehash[IP_CT_DIR_ORIGINAL]; + if (ip_ct_tuple_equal(tuple, &h->ctrack->tuplehash[IP_CT_DIR_REPLY].tuple)) + return &h->ctrack->tuplehash[IP_CT_DIR_REPLY]; +#endif + } + return NULL; } /* Find a connection corresponding to a tuple. */ @@ -390,12 +420,13 @@ const struct ip_conntrack *ignored_conntrack) { struct ip_conntrack_tuple_hash *h; + u_int32_t key = hash_conntrack(tuple); - READ_LOCK(&ip_conntrack_lock); - h = __ip_conntrack_find(tuple, ignored_conntrack); + read_lock_key(key); + h = __ip_conntrack_find(key, tuple, ignored_conntrack); if (h) atomic_inc(&h->ctrack->ct_general.use); - READ_UNLOCK(&ip_conntrack_lock); + read_unlock_key(key); return h; } @@ -421,11 +452,17 @@ return NULL; } +static inline int +conntrack_tuple_cmp(const struct ip_conntrack_tuple_hash *i, + const struct ip_conntrack_tuple *tuple) +{ + return ip_ct_tuple_equal(tuple, &i->tuple); +} + /* Confirm a connection given skb->nfct; places it in hash table */ int __ip_conntrack_confirm(struct nf_ct_info *nfct) { - unsigned int hash, repl_hash; struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; @@ -438,9 +475,6 @@ if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) return NF_ACCEPT; - hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); - /* We're not in hash table, and we refuse to set up related connections for unconfirmed conns. But packet copies and REJECT will give spurious warnings here. */ @@ -451,22 +485,31 @@ IP_NF_ASSERT(!is_confirmed(ct)); DEBUGP("Confirming conntrack %p\n", ct); - WRITE_LOCK(&ip_conntrack_lock); + write_lock_ct(ct); /* See if there's one in the list already, including reverse: NAT could have grabbed it without realizing, since we're not in the hash. If there is, we lost race. */ - if (!LIST_FIND(&ip_conntrack_hash[hash], +#ifdef CONFIG_IP_NF_NAT_NEEDED + if (!LIST_FIND(&ip_conntrack_hash[ct->key[IP_CT_DIR_ORIGINAL]].list, conntrack_tuple_cmp, struct ip_conntrack_tuple_hash *, - &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, NULL) - && !LIST_FIND(&ip_conntrack_hash[repl_hash], + &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple) + && !LIST_FIND(&ip_conntrack_hash[ct->key[IP_CT_DIR_REPLY]].list, conntrack_tuple_cmp, struct ip_conntrack_tuple_hash *, - &ct->tuplehash[IP_CT_DIR_REPLY].tuple, NULL)) { - list_prepend(&ip_conntrack_hash[hash], + &ct->tuplehash[IP_CT_DIR_REPLY].tuple)) { + list_prepend(&ip_conntrack_hash[ct->key[IP_CT_DIR_ORIGINAL]].list, &ct->tuplehash[IP_CT_DIR_ORIGINAL]); - list_prepend(&ip_conntrack_hash[repl_hash], + list_prepend(&ip_conntrack_hash[ct->key[IP_CT_DIR_REPLY]].list, &ct->tuplehash[IP_CT_DIR_REPLY]); +#else + if (!LIST_FIND(&ip_conntrack_hash[ct->key[IP_CT_DIR_ORIGINAL]].list, + conntrack_tuple_cmp, + struct ip_conntrack_tuple_hash *, + &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple)) { + list_prepend(&ip_conntrack_hash[ct->key[IP_CT_DIR_ORIGINAL]].list, + &ct->tuplehash[IP_CT_DIR_ORIGINAL]); +#endif /* Timer relative to confirmation time, not original setting time, otherwise we'd get timer wrap in weird delay cases. */ @@ -474,11 +517,16 @@ add_timer(&ct->timeout); atomic_inc(&ct->ct_general.use); set_bit(IPS_CONFIRMED_BIT, &ct->status); - WRITE_UNLOCK(&ip_conntrack_lock); + write_unlock_ct(ct); + + /* Finally, callback to say expectation is done. */ + if (ct->master && ct->master->expectfn) + return ct->master->expectfn(ct); + return NF_ACCEPT; } - WRITE_UNLOCK(&ip_conntrack_lock); + write_unlock_ct(ct); return NF_DROP; } @@ -489,10 +537,11 @@ const struct ip_conntrack *ignored_conntrack) { struct ip_conntrack_tuple_hash *h; + u_int32_t key = hash_conntrack(tuple); - READ_LOCK(&ip_conntrack_lock); - h = __ip_conntrack_find(tuple, ignored_conntrack); - READ_UNLOCK(&ip_conntrack_lock); + read_lock_key(key); + h = __ip_conntrack_find(key, tuple, ignored_conntrack); + read_unlock_key(key); return h != NULL; } @@ -595,17 +644,19 @@ return !(test_bit(IPS_ASSURED_BIT, &i->ctrack->status)); } -static int early_drop(struct list_head *chain) +static int early_drop(u_int32_t key) { /* Traverse backwards: gives us oldest, which is roughly LRU */ struct ip_conntrack_tuple_hash *h; int dropped = 0; - READ_LOCK(&ip_conntrack_lock); - h = LIST_FIND_B(chain, unreplied, struct ip_conntrack_tuple_hash *); + read_lock_key(key); + h = LIST_FIND_B(&ip_conntrack_hash[key].list, + unreplied, + struct ip_conntrack_tuple_hash *); if (h) atomic_inc(&h->ctrack->ct_general.use); - READ_UNLOCK(&ip_conntrack_lock); + read_unlock_key(key); if (!h) return dropped; @@ -626,9 +677,15 @@ 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); + struct ip_conntrack_helper *h; + + READ_LOCK(&ip_conntrack_lock); + h = LIST_FIND(&helpers, helper_cmp, + struct ip_conntrack_helper *, + tuple); + READ_UNLOCK(&ip_conntrack_lock); + + return h; } /* Allocate a new conntrack: we return -ENOMEM if classification @@ -659,8 +716,7 @@ bomb one hash chain). */ unsigned int next = (drop_next++)%ip_conntrack_htable_size; - if (!early_drop(&ip_conntrack_hash[next]) - && !early_drop(&ip_conntrack_hash[hash])) { + if (!early_drop(next) && !early_drop(hash)) { if (net_ratelimit()) printk(KERN_WARNING "ip_conntrack: table full, dropping" @@ -687,6 +743,10 @@ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].ctrack = conntrack; conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple; conntrack->tuplehash[IP_CT_DIR_REPLY].ctrack = conntrack; + conntrack->key[IP_CT_DIR_ORIGINAL] = hash; +#ifdef CONFIG_IP_NF_NAT_NEEDED + conntrack->key[IP_CT_DIR_REPLY] = hash_conntrack(&repl_tuple); +#endif for (i=0; i < IP_CT_NUMBER; i++) conntrack->infos[i].master = &conntrack->ct_general; @@ -701,30 +761,26 @@ INIT_LIST_HEAD(&conntrack->sibling_list); - WRITE_LOCK(&ip_conntrack_lock); - /* Need finding and deleting of expected ONLY if we win race */ - READ_LOCK(&ip_conntrack_expect_tuple_lock); - expected = LIST_FIND(&ip_conntrack_expect_list, expect_cmp, - struct ip_conntrack_expect *, tuple); - READ_UNLOCK(&ip_conntrack_expect_tuple_lock); + READ_LOCK(&ip_conntrack_expect_lock); + expected = __ip_ct_expect_find(tuple); + + /* Look up the conntrack helper for master connections only: */ + if (!expected) + conntrack->helper = ip_ct_find_helper(&repl_tuple); /* If master is not in hash table yet (ie. packet hasn't left this machine yet), how can other end know about expected? Hence these are not the droids you are looking for (if master ct never got confirmed, we'd hold a reference to it - and weird things would happen to future packets). */ - if (expected && !is_confirmed(expected->expectant)) - expected = NULL; - - /* Look up the conntrack helper for master connections only */ - if (!expected) - conntrack->helper = ip_ct_find_helper(&repl_tuple); + and weird things would happen to future packets). - /* If the expectation is dying, then this is a looser. */ - if (expected - && expected->expectant->helper->timeout - && ! del_timer(&expected->timeout)) + If we can delete the timer, then unexpect_related or + remove_expectations cannot delete the expectation + from the global/local lists anymore. Success! */ + else if (!is_confirmed(expected->expectant) + || !del_timer(&expected->timeout)) expected = NULL; + READ_UNLOCK(&ip_conntrack_expect_lock); if (expected) { DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", @@ -733,16 +789,16 @@ IP_NF_ASSERT(master_ct(conntrack)); __set_bit(IPS_EXPECTED_BIT, &conntrack->status); conntrack->master = expected; + + WRITE_LOCK(&ip_conntrack_expect_lock); expected->sibling = conntrack; LIST_DELETE(&ip_conntrack_expect_list, expected); expected->expectant->expecting--; - nf_conntrack_get(&master_ct(conntrack)->infos[0]); + atomic_inc(&expected->use); + WRITE_UNLOCK(&ip_conntrack_expect_lock); } atomic_inc(&ip_conntrack_count); - WRITE_UNLOCK(&ip_conntrack_lock); - if (expected && expected->expectfn) - expected->expectfn(conntrack); return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL]; } @@ -859,11 +915,12 @@ IP_NF_ASSERT((*pskb)->nfct); ret = proto->packet(ct, (*pskb)->nh.iph, (*pskb)->len, ctinfo); - if (ret == -1) { - /* Invalid */ + if (ret < 0 ) { + /* Invalid: inverse of the return code tells + * to the netfilter core what to do. */ nf_conntrack_put((*pskb)->nfct); (*pskb)->nfct = NULL; - return NF_ACCEPT; + return -ret; } if (ret != NF_DROP && ct->helper) { @@ -918,11 +975,9 @@ return ip_ct_tuple_mask_cmp(&i->tuple, tuple, &intersect_mask); } -inline void ip_conntrack_unexpect_related(struct ip_conntrack_expect *expect) +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) @@ -930,9 +985,7 @@ struct ip_conntrack_expect *expect = (void *) ul_expect; DEBUGP("expectation %p timed out\n", expect); - WRITE_LOCK(&ip_conntrack_lock); __unexpect_related(expect); - WRITE_UNLOCK(&ip_conntrack_lock); } /* Add a related connection. */ @@ -942,43 +995,34 @@ struct ip_conntrack_expect *old, *new; int ret = 0; - 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 */ + WRITE_LOCK(&ip_conntrack_expect_lock); DEBUGP("ip_conntrack_expect_related %p\n", related_to); DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple); DEBUGP("mask: "); DUMP_TUPLE(&expect->mask); old = LIST_FIND(&ip_conntrack_expect_list, resent_expect, - struct ip_conntrack_expect *, &expect->tuple, - &expect->mask); + struct ip_conntrack_expect *, + &expect->tuple, &expect->mask); if (old) { /* 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) { - if (!del_timer(&old->timeout)) { - /* expectation is dying. Fall through */ - old = NULL; - } else { - old->timeout.expires = jiffies + - related_to->helper->timeout * HZ; - add_timer(&old->timeout); - } - } - - if (old) { - WRITE_UNLOCK(&ip_conntrack_lock); + if (del_timer(&old->timeout)) { + old->timeout.expires = jiffies + + related_to->helper->timeout * HZ; + add_timer(&old->timeout); + WRITE_UNLOCK(&ip_conntrack_expect_lock); return -EEXIST; } + /* expectation is dying. Fall through */ } else if (related_to->helper->max_expected && related_to->expecting >= related_to->helper->max_expected) { - /* old == NULL */ + struct list_head *entry; if (!(related_to->helper->flags & IP_CT_HELPER_F_REUSE_EXPECT)) { - WRITE_UNLOCK(&ip_conntrack_lock); + WRITE_UNLOCK(&ip_conntrack_expect_lock); if (net_ratelimit()) printk(KERN_WARNING "ip_conntrack: max number of expected " @@ -999,10 +1043,13 @@ NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip)); /* choose the the oldest expectation to evict */ - list_for_each_entry(old, &related_to->sibling_list, - expected_list) + list_for_each_prev(entry, &related_to->sibling_list) { + old = list_entry(entry, + struct ip_conntrack_expect, + expected_list); if (old->sibling == NULL) break; + } /* We cannot fail since related_to->expecting is the number * of unconfirmed expectations */ @@ -1010,15 +1057,16 @@ /* newnat14 does not reuse the real allocated memory * structures but rather unexpects the old and - * allocates a new. unexpect_related will decrement - * related_to->expecting. + * allocates a new. ip_conntrack_expect_put will decrement + * the refcount we hold against our creator. */ - unexpect_related(old); + del_expect_from_lists(old); + ip_conntrack_expect_put(old); ret = -EPERM; } else if (LIST_FIND(&ip_conntrack_expect_list, expect_clash, struct ip_conntrack_expect *, &expect->tuple, &expect->mask)) { - WRITE_UNLOCK(&ip_conntrack_lock); + WRITE_UNLOCK(&ip_conntrack_expect_lock); DEBUGP("expect_related: busy!\n"); return -EBUSY; } @@ -1026,34 +1074,42 @@ new = (struct ip_conntrack_expect *) kmalloc(sizeof(struct ip_conntrack_expect), GFP_ATOMIC); if (!new) { - WRITE_UNLOCK(&ip_conntrack_lock); - DEBUGP("expect_relaed: OOM allocating expect\n"); + WRITE_UNLOCK(&ip_conntrack_expect_lock); + DEBUGP("expect_related: OOM allocating expect\n"); return -ENOMEM; } + /* Initialize the data */ DEBUGP("new expectation %p of conntrack %p\n", new, related_to); - memcpy(new, expect, sizeof(*expect)); + memcpy(new, expect, sizeof(*new)); new->expectant = related_to; new->sibling = NULL; atomic_set(&new->use, 1); - + memset(&new->ct_tuple, 0, sizeof(new->ct_tuple)); + +/* Default timeout: one day */ +#define DEFAULT_EXPECTATION_TIMEOUT 1*24*60*60 + + init_timer(&new->timeout); + new->timeout.data = (unsigned long)new; + new->timeout.function = expectation_timed_out; + new->timeout.expires = jiffies + + (related_to->helper->timeout ? related_to->helper->timeout + : DEFAULT_EXPECTATION_TIMEOUT) * HZ; + + /* increase refcount of master */ + atomic_inc(&related_to->ct_general.use); /* add to expected list for this connection */ - list_add_tail(&new->expected_list, &related_to->sibling_list); + list_add(&new->expected_list, &related_to->sibling_list); /* add to global list of expectations */ - list_prepend(&ip_conntrack_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_add(&new->list, &ip_conntrack_expect_list); - WRITE_UNLOCK(&ip_conntrack_lock); + related_to->expecting++; + WRITE_UNLOCK(&ip_conntrack_expect_lock); + /* Finally start timer */ + add_timer(&new->timeout); + return ret; } @@ -1063,8 +1119,7 @@ { int ret; - MUST_BE_READ_LOCKED(&ip_conntrack_lock); - WRITE_LOCK(&ip_conntrack_expect_tuple_lock); + WRITE_LOCK(&ip_conntrack_expect_lock); DEBUGP("change_expect:\n"); DEBUGP("exp tuple: "); DUMP_TUPLE(&expect->tuple); @@ -1093,7 +1148,7 @@ ret = -1; } } - WRITE_UNLOCK(&ip_conntrack_expect_tuple_lock); + WRITE_UNLOCK(&ip_conntrack_expect_lock); return ret; } @@ -1103,23 +1158,28 @@ int ip_conntrack_alter_reply(struct ip_conntrack *conntrack, const struct ip_conntrack_tuple *newreply) { - WRITE_LOCK(&ip_conntrack_lock); - if (__ip_conntrack_find(newreply, conntrack)) { - WRITE_UNLOCK(&ip_conntrack_lock); +#ifdef CONFIG_IP_NF_NAT_NEEDED + u_int32_t key = hash_conntrack(newreply); + + /* Should be unconfirmed, so not in hash table yet. */ + IP_NF_ASSERT(!is_confirmed(conntrack)); + /* So who else could modify it besides us? */ + + read_lock_key(key); + if (__ip_conntrack_find(key, newreply, conntrack)) { + read_unlock_key(key); return 0; } - /* Should be unconfirmed, so not in hash table yet */ - IP_NF_ASSERT(!is_confirmed(conntrack)); + read_unlock_key(key); DEBUGP("Altering reply tuple of %p to ", conntrack); DUMP_TUPLE(newreply); conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; + conntrack->key[IP_CT_DIR_REPLY] = key; if (!conntrack->master) - conntrack->helper = LIST_FIND(&helpers, helper_cmp, - struct ip_conntrack_helper *, - newreply); - WRITE_UNLOCK(&ip_conntrack_lock); + conntrack->helper = ip_ct_find_helper(newreply); +#endif return 1; } @@ -1135,12 +1195,12 @@ return 0; } -static inline int unhelp(struct ip_conntrack_tuple_hash *i, +static inline int unhelp(const struct ip_conntrack_tuple_hash *i, const struct ip_conntrack_helper *me) { if (i->ctrack->helper == me) { /* Get rid of any expected. */ - remove_expectations(i->ctrack, 0); + remove_expectations(i->ctrack); /* And *then* set helper to NULL */ i->ctrack->helper = NULL; } @@ -1154,17 +1214,20 @@ /* Need write lock here, to delete helper. */ WRITE_LOCK(&ip_conntrack_lock); LIST_DELETE(&helpers, me); - - /* Get rid of expecteds, set helpers to NULL. */ - for (i = 0; i < ip_conntrack_htable_size; i++) - LIST_FIND_W(&ip_conntrack_hash[i], unhelp, - struct ip_conntrack_tuple_hash *, me); WRITE_UNLOCK(&ip_conntrack_lock); /* Someone could be still looking at the helper in a bh. */ br_write_lock_bh(BR_NETPROTO_LOCK); br_write_unlock_bh(BR_NETPROTO_LOCK); + /* Get rid of expecteds, set helpers to NULL. */ + for (i = 0; i < ip_conntrack_htable_size; i++) { + read_lock_key(i); + LIST_FIND(&ip_conntrack_hash[i].list, unhelp, + struct ip_conntrack_tuple_hash *, me); + read_unlock_key(i); + } + MOD_DEC_USE_COUNT; } @@ -1173,7 +1236,7 @@ { IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct); - WRITE_LOCK(&ip_conntrack_lock); + write_lock_ct(ct); /* If not in hash table, timer will not be active yet */ if (!is_confirmed(ct)) ct->timeout.expires = extra_jiffies; @@ -1184,7 +1247,7 @@ add_timer(&ct->timeout); } } - WRITE_UNLOCK(&ip_conntrack_lock); + write_unlock_ct(ct); } /* Returns new sk_buff, or NULL */ @@ -1262,14 +1325,14 @@ { struct ip_conntrack_tuple_hash *h = NULL; - READ_LOCK(&ip_conntrack_lock); for (; !h && *bucket < ip_conntrack_htable_size; (*bucket)++) { - h = LIST_FIND(&ip_conntrack_hash[*bucket], do_kill, + read_lock_key(*bucket); + h = LIST_FIND(&ip_conntrack_hash[*bucket].list, do_kill, struct ip_conntrack_tuple_hash *, kill, data); + if (h) + atomic_inc(&h->ctrack->ct_general.use); + read_unlock_key(*bucket); } - if (h) - atomic_inc(&h->ctrack->ct_general.use); - READ_UNLOCK(&ip_conntrack_lock); return h; } @@ -1412,7 +1475,7 @@ return ret; } - ip_conntrack_hash = vmalloc(sizeof(struct list_head) + ip_conntrack_hash = vmalloc(sizeof(struct ip_conntrack_hash) * ip_conntrack_htable_size); if (!ip_conntrack_hash) { printk(KERN_ERR "Unable to create ip_conntrack_hash\n"); @@ -1434,8 +1497,10 @@ list_append(&protocol_list, &ip_conntrack_protocol_icmp); WRITE_UNLOCK(&ip_conntrack_lock); - for (i = 0; i < ip_conntrack_htable_size; i++) - INIT_LIST_HEAD(&ip_conntrack_hash[i]); + for (i = 0; i < ip_conntrack_htable_size; i++) { + INIT_LIST_HEAD(&ip_conntrack_hash[i].list); + rwlock_init(&ip_conntrack_hash[i].lock); + } /* For use by ipt_REJECT */ ip_ct_attach = ip_conntrack_attach; diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_egg.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_egg.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_egg.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_egg.c Sun Apr 18 17:33:06 2004 @@ -0,0 +1,237 @@ +/* Eggdrop extension for IP connection tracking, Version 0.0.5 + * based on ip_conntrack_irc.c + * + * This module only supports the share userfile-send command, + * used by eggdrops to share it's userfile. + * + * There are no support for NAT at the moment. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Module load syntax: + * + * please give the ports of all Eggdrops You have running + * on your system, the default port is 3333. + * + * 2001-04-19: Security update. IP addresses are now compared + * to prevent unauthorized "related" access. + * + * 2002-03-25: Harald Welte : + * 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 deregister_helpers(void) { + int i; + + for (i = 0; i < ports_c; i++) { + DEBUGP("unregistering helper for port %d\n", ports[i]); + ip_conntrack_helper_unregister(&egg_helpers[i]); + } +} + +static int __init init(void) +{ + int i, ret; + char *tmpname; + + /* If no port given, default to standard eggdrop port */ + if (ports[0] == 0) + ports[0] = 3333; + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + memset(&egg_helpers[i], 0, + sizeof(struct ip_conntrack_helper)); + egg_helpers[i].tuple.src.u.tcp.port = htons(ports[i]); + egg_helpers[i].tuple.dst.protonum = IPPROTO_TCP; + egg_helpers[i].mask.src.u.tcp.port = 0xFFFF; + egg_helpers[i].mask.dst.protonum = 0xFFFF; + egg_helpers[i].max_expected = 1; + egg_helpers[i].timeout = egg_timeout; + egg_helpers[i].flags = IP_CT_HELPER_F_REUSE_EXPECT; + egg_helpers[i].me = THIS_MODULE; + egg_helpers[i].help = help; + + tmpname = &egg_names[i][0]; + if (ports[i] == 3333) + sprintf(tmpname, "eggdrop"); + else + sprintf(tmpname, "eggdrop-%d", ports[i]); + egg_helpers[i].name = tmpname; + + DEBUGP("port #%d: %d\n", i, ports[i]); + + ret = ip_conntrack_helper_register(&egg_helpers[i]); + + if (ret) { + printk("ip_conntrack_egg: ERROR registering helper " + "for port %d\n", ports[i]); + deregister_helpers(); + return 1; + } + ports_c++; + } + return 0; +} + +static void __exit fini(void) +{ + deregister_helpers(); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_ftp.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_ftp.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_ftp.c Wed Jul 30 09:19:06 2003 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_ftp.c Sun Apr 18 17:33:11 2004 @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -233,11 +232,10 @@ struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { - /* tcplen not negative guaranteed by ip_conntrack_tcp.c */ + /* datalen not negative guaranteed by ip_conntrack_proto_tcp.c */ struct tcphdr *tcph = (void *)iph + iph->ihl * 4; const char *data = (const char *)tcph + tcph->doff * 4; - unsigned int tcplen = len - iph->ihl * 4; - unsigned int datalen = tcplen - tcph->doff * 4; + unsigned int datalen = len - iph->ihl * 4 - tcph->doff * 4; u_int32_t old_seq_aft_nl; int old_seq_aft_nl_set; u_int32_t array[6] = { 0 }; @@ -254,22 +252,6 @@ if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { DEBUGP("ftp: Conntrackinfo = %u\n", ctinfo); - return NF_ACCEPT; - } - - /* Not whole TCP header? */ - if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) { - DEBUGP("ftp: 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("ftp_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; } diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_h323.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_h323.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_h323.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_h323.c Sun Apr 18 17:33:06 2004 @@ -0,0 +1,308 @@ +/* + * 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, + { .tcp = { data_port } }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); + + exp->expectfn = NULL; + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, exp); + + UNLOCK_BH(&ip_h323_lock); + } + } + + return NF_ACCEPT; + +} + +/* H.245 helper is not registered! */ +static struct ip_conntrack_helper h245 = + { { NULL, NULL }, + "H.245", /* name */ + IP_CT_HELPER_F_REUSE_EXPECT, /* flags */ + NULL, /* module */ + 8, /* max_ expected */ + 240, /* timeout */ + { { 0, { 0 } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h245_help /* helper */ + }; + +static int h225_expect(struct ip_conntrack *ct) +{ + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &h245; + DEBUGP("h225_expect: helper for %p added\n", ct); + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +/* FIXME: This should be in userspace. Later. */ +static int h225_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + unsigned char *data = (unsigned char *) tcph + tcph->doff * 4; + unsigned char *data_limit; + u_int32_t tcplen = len - iph->ihl * 4; + u_int32_t datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info; + u_int16_t data_port; + u_int32_t data_ip; + unsigned int i; + + DEBUGP("ct_h225_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), + NIPQUAD(iph->daddr), ntohs(tcph->dest)); + + /* Can't track connections formed before we registered */ + if (!info) + return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ct_h225_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header or too short packet? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) { + DEBUGP("ct_h225_help: tcplen = %u\n", (unsigned)tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcplen, 0))) { + DEBUGP("ct_h225_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + data_limit = (unsigned char *) data + datalen; + /* bytes: 0123 45 + ipadrr port */ + for (i = 0; data < (data_limit - 5); data++, i++) { + data_ip = *((u_int32_t *)data); + if (data_ip == iph->saddr) { + data_port = *((u_int16_t *)(data + 4)); + if (data_port == tcph->source) { + /* Signal address */ + DEBUGP("ct_h225_help: sourceCallSignalAddress from %u.%u.%u.%u\n", + NIPQUAD(iph->saddr)); + /* Update the H.225 info so that NAT can mangle the address/port + even when we have no expected connection! */ +#ifdef CONFIG_IP_NF_NAT_NEEDED + LOCK_BH(&ip_h323_lock); + info->dir = dir; + info->seq[IP_CT_DIR_ORIGINAL] = ntohl(tcph->seq) + i; + info->offset[IP_CT_DIR_ORIGINAL] = i; + UNLOCK_BH(&ip_h323_lock); +#endif + } else { + memset(&expect, 0, sizeof(expect)); + + /* update the H.225 info */ + LOCK_BH(&ip_h323_lock); + info->is_h225 = H225_PORT; + exp_info->port = data_port; + exp_info->dir = dir; + exp_info->offset = i; + + exp->seq = ntohl(tcph->seq) + i; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { data_ip, + { .tcp = { data_port } }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); + + exp->expectfn = h225_expect; + + /* Ignore failure */ + ip_conntrack_expect_related(ct, exp); + + DEBUGP("ct_h225_help: new H.245 requested %u.%u.%u.%u->%u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), + NIPQUAD(iph->saddr), ntohs(data_port)); + + UNLOCK_BH(&ip_h323_lock); + } +#ifdef CONFIG_IP_NF_NAT_NEEDED + } else if (data_ip == iph->daddr) { + data_port = *((u_int16_t *)(data + 4)); + if (data_port == tcph->dest) { + /* Signal address */ + DEBUGP("ct_h225_help: destCallSignalAddress %u.%u.%u.%u\n", + NIPQUAD(iph->daddr)); + /* Update the H.225 info so that NAT can mangle the address/port + even when we have no expected connection! */ + LOCK_BH(&ip_h323_lock); + info->dir = dir; + info->seq[IP_CT_DIR_REPLY] = ntohl(tcph->seq) + i; + info->offset[IP_CT_DIR_REPLY] = i; + UNLOCK_BH(&ip_h323_lock); + } +#endif + } + } + + return NF_ACCEPT; + +} + +static struct ip_conntrack_helper h225 = + { { NULL, NULL }, + "H.225", /* name */ + IP_CT_HELPER_F_REUSE_EXPECT, /* flags */ + THIS_MODULE, /* module */ + 2, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(H225_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h225_help /* helper */ + }; + +static int __init init(void) +{ + return ip_conntrack_helper_register(&h225); +} + +static void __exit fini(void) +{ + /* Unregister H.225 helper */ + ip_conntrack_helper_unregister(&h225); +} + +EXPORT_SYMBOL(ip_h323_lock); + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_irc.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_irc.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_irc.c Fri Oct 10 08:47:16 2003 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_irc.c Sun Apr 18 17:33:11 2004 @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -109,13 +108,12 @@ 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 */ + /* datalen not negative guarenteed by ip_conntrack_proto_tcp.c */ struct tcphdr *tcph = (void *) iph + iph->ihl * 4; const char *data = (const char *) tcph + tcph->doff * 4; const char *_data = data; char *data_limit; - u_int32_t tcplen = len - iph->ihl * 4; - u_int32_t datalen = tcplen - tcph->doff * 4; + u_int32_t datalen = len - iph->ihl * 4 - tcph->doff * 4; int dir = CTINFO2DIR(ctinfo); struct ip_conntrack_expect expect, *exp = &expect; struct ip_ct_irc_expect *exp_irc_info = &exp->help.exp_irc_info; @@ -135,22 +133,6 @@ 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; } diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_mms.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_mms.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_mms.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_mms.c Sun Apr 18 17:33:07 2004 @@ -0,0 +1,308 @@ +/* MMS extension for IP connection tracking + * (C) 2002 by Filip Sneppe + * based on ip_conntrack_ftp.c and ip_conntrack_irc.c + * + * ip_conntrack_mms.c v0.3 2002-09-22 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Module load syntax: + * insmod ip_conntrack_mms.o ports=port1,port2,...port + * + * Please give the ports of all MMS servers You wish to connect to. + * If you don't specify ports, the default will be TCP port 1755. + * + * More info on MMS protocol, firewalls and NAT: + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/MMSFirewall.asp + * http://www.microsoft.com/windows/windowsmedia/serve/firewall.asp + * + * The SDP project people are reverse-engineering MMS: + * http://get.to/sdp + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +DECLARE_LOCK(ip_mms_lock); +struct module *ip_conntrack_mms = THIS_MODULE; + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_c; +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +#endif + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +EXPORT_SYMBOL(ip_mms_lock); + +MODULE_AUTHOR("Filip Sneppe "); +MODULE_DESCRIPTION("Microsoft Windows Media Services (MMS) connection tracking module"); +MODULE_LICENSE("GPL"); + +/* #define isdigit(c) (c >= '0' && c <= '9') */ + +/* copied from drivers/usb/serial/io_edgeport.c - not perfect but will do the trick */ +static void unicode_to_ascii (char *string, short *unicode, int unicode_size) +{ + int i; + for (i = 0; i < unicode_size; ++i) { + string[i] = (char)(unicode[i]); + } + string[unicode_size] = 0x00; +} + +__inline static int atoi(char *s) +{ + int i=0; + while (isdigit(*s)) { + i = i*10 + *(s++) - '0'; + } + return i; +} + +/* convert ip address string like "192.168.0.10" to unsigned int */ +__inline static u_int32_t asciiiptoi(char *s) +{ + unsigned int i, j, k; + + for(i=k=0; k<3; ++k, ++s, i<<=8) { + i+=atoi(s); + for(j=0; (*(++s) != '.') && (j<3); ++j) + ; + } + i+=atoi(s); + return ntohl(i); +} + +int parse_mms(const char *data, + const unsigned int datalen, + u_int32_t *mms_ip, + u_int16_t *mms_proto, + u_int16_t *mms_port, + char **mms_string_b, + char **mms_string_e, + char **mms_padding_e) +{ + int unicode_size, i; + char tempstring[28]; /* "\\255.255.255.255\UDP\65535" */ + char getlengthstring[28]; + + for(unicode_size=0; + (char) *(data+(MMS_SRV_UNICODE_STRING_OFFSET+unicode_size*2)) != (char)0; + unicode_size++) + if ((unicode_size == 28) || (MMS_SRV_UNICODE_STRING_OFFSET+unicode_size*2 >= datalen)) + return -1; /* out of bounds - incomplete packet */ + + unicode_to_ascii(tempstring, (short *)(data+MMS_SRV_UNICODE_STRING_OFFSET), unicode_size); + DEBUGP("ip_conntrack_mms: offset 60: %s\n", (const char *)(tempstring)); + + /* IP address ? */ + *mms_ip = asciiiptoi(tempstring+2); + + i=sprintf(getlengthstring, "%u.%u.%u.%u", HIPQUAD(*mms_ip)); + + /* protocol ? */ + if(strncmp(tempstring+3+i, "TCP", 3)==0) + *mms_proto = IPPROTO_TCP; + else if(strncmp(tempstring+3+i, "UDP", 3)==0) + *mms_proto = IPPROTO_UDP; + + /* port ? */ + *mms_port = atoi(tempstring+7+i); + + /* we store a pointer to the beginning of the "\\a.b.c.d\proto\port" + unicode string, one to the end of the string, and one to the end + of the packet, since we must keep track of the number of bytes + between end of the unicode string and the end of packet (padding) */ + *mms_string_b = (char *)(data + MMS_SRV_UNICODE_STRING_OFFSET); + *mms_string_e = (char *)(data + MMS_SRV_UNICODE_STRING_OFFSET + unicode_size * 2); + *mms_padding_e = (char *)(data + datalen); /* looks funny, doesn't it */ + return 0; +} + + +/* FIXME: This should be in userspace. Later. */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + /* tcplen not negative guaranteed by ip_conntrack_tcp.c */ + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + const char *data = (const char *)tcph + tcph->doff * 4; + unsigned int tcplen = len - iph->ihl * 4; + unsigned int datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_mms_expect *exp_mms_info = &exp->help.exp_mms_info; + + u_int32_t mms_ip; + u_int16_t mms_proto; + char mms_proto_string[8]; + u_int16_t mms_port; + char *mms_string_b, *mms_string_e, *mms_padding_e; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { + DEBUGP("ip_conntrack_mms: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) { + DEBUGP("ip_conntrack_mms: tcplen = %u\n", (unsigned)tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcplen, 0))) { + DEBUGP("mms_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + /* Only look at packets with 0x00030002/196610 on bytes 36->39 of TCP payload */ + /* FIXME: There is an issue with only looking at this packet: before this packet, + the client has already sent a packet to the server with the server's hostname + according to the client (think of it as the "Host: " header in HTTP/1.1). The + server will break the connection if this doesn't correspond to its own host + header. The client can also connect to an IP address; if it's the server's IP + address, it will not break the connection. When doing DNAT on a connection + where the client uses a server's IP address, the nat module should detect + this and change this string accordingly to the DNATed address. This should + probably be done by checking for an IP address, then storing it as a member + of struct ip_ct_mms_expect and checking for it in ip_nat_mms... + */ + if( (MMS_SRV_MSG_OFFSET < datalen) && + ((*(u32 *)(data+MMS_SRV_MSG_OFFSET)) == MMS_SRV_MSG_ID)) { + DEBUGP("ip_conntrack_mms: offset 37: %u %u %u %u, datalen:%u\n", + (u8)*(data+36), (u8)*(data+37), + (u8)*(data+38), (u8)*(data+39), + datalen); + if(parse_mms(data, datalen, &mms_ip, &mms_proto, &mms_port, + &mms_string_b, &mms_string_e, &mms_padding_e)) + if(net_ratelimit()) + /* FIXME: more verbose debugging ? */ + printk(KERN_WARNING + "ip_conntrack_mms: Unable to parse data payload\n"); + + memset(&expect, 0, sizeof(expect)); + + sprintf(mms_proto_string, "(%u)", mms_proto); + DEBUGP("ip_conntrack_mms: adding %s expectation %u.%u.%u.%u -> %u.%u.%u.%u:%u\n", + mms_proto == IPPROTO_TCP ? "TCP" + : mms_proto == IPPROTO_UDP ? "UDP":mms_proto_string, + NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), + NIPQUAD(mms_ip), + mms_port); + + /* it's possible that the client will just ask the server to tunnel + the stream over the same TCP session (from port 1755): there's + shouldn't be a need to add an expectation in that case, but it + makes NAT packet mangling so much easier */ + LOCK_BH(&ip_mms_lock); + + DEBUGP("ip_conntrack_mms: tcph->seq = %u\n", tcph->seq); + + exp->seq = ntohl(tcph->seq) + (mms_string_b - data); + exp_mms_info->len = (mms_string_e - mms_string_b); + exp_mms_info->padding = (mms_padding_e - mms_string_e); + exp_mms_info->port = mms_port; + + DEBUGP("ip_conntrack_mms: wrote info seq=%u (ofs=%u), len=%d, padding=%u\n", + exp->seq, (mms_string_e - data), exp_mms_info->len, exp_mms_info->padding); + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, { 0 } }, + { mms_ip, + { .tcp = { (__u16) ntohs(mms_port) } }, + mms_proto } } + ); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); + exp->expectfn = NULL; + ip_conntrack_expect_related(ct, &expect); + UNLOCK_BH(&ip_mms_lock); + } + + return NF_ACCEPT; +} + +static struct ip_conntrack_helper mms[MAX_PORTS]; +static char mms_names[MAX_PORTS][10]; + +/* Not __exit: called from init() */ +static void fini(void) +{ + int i; + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + DEBUGP("ip_conntrack_mms: unregistering helper for port %d\n", + ports[i]); + ip_conntrack_helper_unregister(&mms[i]); + } +} + +static int __init init(void) +{ + int i, ret; + char *tmpname; + + if (ports[0] == 0) + ports[0] = MMS_PORT; + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + memset(&mms[i], 0, sizeof(struct ip_conntrack_helper)); + mms[i].tuple.src.u.tcp.port = htons(ports[i]); + mms[i].tuple.dst.protonum = IPPROTO_TCP; + mms[i].mask.src.u.tcp.port = 0xFFFF; + mms[i].mask.dst.protonum = 0xFFFF; + mms[i].max_expected = 1; + mms[i].timeout = 0; + mms[i].flags = IP_CT_HELPER_F_REUSE_EXPECT; + mms[i].me = THIS_MODULE; + mms[i].help = help; + + tmpname = &mms_names[i][0]; + if (ports[i] == MMS_PORT) + sprintf(tmpname, "mms"); + else + sprintf(tmpname, "mms-%d", ports[i]); + mms[i].name = tmpname; + + DEBUGP("ip_conntrack_mms: registering helper for port %d\n", + ports[i]); + ret = ip_conntrack_helper_register(&mms[i]); + + if (ret) { + fini(); + return ret; + } + ports_c++; + } + return 0; +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_proto_tcp.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_proto_tcp.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_proto_tcp.c Wed Nov 19 15:31:23 2003 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_proto_tcp.c Sun Apr 18 17:33:11 2004 @@ -1,3 +1,20 @@ +/* + * TCP connection tracking + */ + +/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General + * Public Licence. + * + * Jozsef Kadlecsik : + * - Real stateful connection tracking + * - Modified state transitions table + * - Window scaling support added + * + * version 2.1 + */ + +#define __NO_VERSION__ +#include #include #include #include @@ -6,101 +23,268 @@ #include #include #include -#include +#include #include #include #include +#include #include #if 0 #define DEBUGP printk +#define DEBUGP_VARS +#define NET_RATELIMIT(foo) (foo) #else #define DEBUGP(format, args...) +#define NET_RATELIMIT(foo) ((foo) && net_ratelimit()) #endif /* Protects conntrack->proto.tcp */ static DECLARE_RWLOCK(tcp_lock); +/* Log invalid/ignored packets */ +int ip_ct_tcp_log_invalid = 1; + +/* "Be conservative in what you do, + be liberal in what you accept from others." + If it's non-zero, we mark only out of window RST segments as INVALID. */ +int ip_ct_tcp_be_liberal = 0; + +/* When connection is picked up from the middle, how many packets are required + to pass in each direction when we assume we are in sync - if any side uses + window scaling, we lost the game. + If it is set to zero, we disable picking up already established connections. */ +int ip_ct_tcp_loose = 3; + +/* Max number of the retransmitted packets without receiving an (acceptable) + ACK from the destination. If this number is reached, a shorter timer + will be started. */ +int ip_ct_tcp_max_retrans = 3; + /* FIXME: Examine ipfilter's timeouts and conntrack transitions more closely. They're more complex. --RR */ -/* Actually, I believe that neither ipmasq (where this code is stolen - from) nor ipfilter do it exactly right. A new conntrack machine taking - into account packet loss (which creates uncertainty as to exactly - the conntrack of the connection) is required. RSN. --RR */ - 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 -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 = 60 SECS; -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_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 = 10 MINS; +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; + +/* RFC1122 says the R2 limit should be at least 100 seconds. + Linux uses 15 packets as limit, which corresponds + to ~13-30min depending on RTO. */ +unsigned long ip_ct_tcp_timeout_max_retrans = 5 MINS; static unsigned long * tcp_timeouts[] -= { 0, /* TCP_CONNTRACK_NONE */ - &ip_ct_tcp_timeout_established, /* TCP_CONNTRACK_ESTABLISHED, */ - &ip_ct_tcp_timeout_syn_sent, /* TCP_CONNTRACK_SYN_SENT, */ - &ip_ct_tcp_timeout_syn_recv, /* TCP_CONNTRACK_SYN_RECV, */ - &ip_ct_tcp_timeout_fin_wait, /* TCP_CONNTRACK_FIN_WAIT, */ - &ip_ct_tcp_timeout_time_wait, /* TCP_CONNTRACK_TIME_WAIT, */ - &ip_ct_tcp_timeout_close, /* TCP_CONNTRACK_CLOSE, */ - &ip_ct_tcp_timeout_close_wait, /* TCP_CONNTRACK_CLOSE_WAIT, */ - &ip_ct_tcp_timeout_last_ack, /* TCP_CONNTRACK_LAST_ACK, */ - 0, /* TCP_CONNTRACK_LISTEN */ - }; - += { 0, /* 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, */ + 0, /* 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 +#define sIG TCP_CONNTRACK_IGNORE -static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = { +/* What TCP flags are set from RST/SYN/FIN/ACK. */ +enum tcp_bit_set { + TCP_SYN_SET, + TCP_SYNACK_SET, + TCP_FIN_SET, + TCP_ACK_SET, + TCP_RST_SET, + TCP_NONE_SET, +}; + +/* + * 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, but our windows are *not* + * equivalent with the ones of the sender/receiver. We always + * try to guess the state of the current sender. + * + * 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. + * + * Packets marked as IGNORED (sIG): + * if they may be either invalid or valid + * and the receiver may send back a connection + * closing RST or a SYN/ACK. + * + * Packets marked as INVALID (sIV): + * if they are invalid + * or we do not support the request (simultaneous open) + */ +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, sIG, sIG, sIG, sIG, sIG, sSS, sSS, sIV }, +/* + * sNO -> sSS Initialize a new connection + * sSS -> sSS Retransmitted SYN + * sSR -> sIG Late retransmitted SYN? + * sES -> sIG Error: SYNs in window outside the SYN_SENT state + * are errors. Receiver will reply with RST + * and close the connection. + * Or we are not in sync and hold a dead connection. + * sFW -> sIG + * sCW -> sIG + * sLA -> sIG + * sTW -> sSS Reopened connection (RFC 1122). + * sCL -> sSS + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*synack*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }, +/* + * A SYN/ACK from the client is always invalid: + * - either it tries to set up a simultaneous open, which is + * not supported; + * - or the firewall has just been inserted between the two hosts + * during the session set-up. The SYN will be retransmitted + * by the true client (or it'll time out). + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*fin*/ { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV }, +/* + * sNO -> sIV Too late and no reason to do anything... + * sSS -> sIV Client migth not send FIN in this state: + * we enforce waiting for a SYN/ACK reply first. + * 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*/ { sIV, 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, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }, +/* + * sNO -> sIV Never reached. + * sSS -> sIV Simultaneous open, not supported + * sSR -> sIV Simultaneous open, not supported. + * sES -> sIV Server may not initiate a connection. + * sFW -> sIV + * sCW -> sIV + * sLA -> sIV + * sTW -> sIV Reopened connection, but server may not do it. + * sCL -> sIV + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*synack*/ { sIV, sSR, sSR, sIG, sIG, sIG, sIG, sIG, sIG, sIV }, +/* + * sSS -> sSR Standard open. + * sSR -> sSR Retransmitted SYN/ACK. + * sES -> sIG Late retransmitted SYN/ACK? + * sFW -> sIG + * sCW -> sIG + * sLA -> sIG + * sTW -> sIG + * sCL -> sIG + */ +/* 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, sIV, sES, sCW, sCW, sTW, sTW, sCL, sIV }, +/* + * sSS -> sIV ACK is invalid: we haven't seen a SYN/ACK yet. + * sSR -> sIV 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, sCL, sCL, sCL, sCL, sIV }, +/*none*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } } }; @@ -145,13 +329,370 @@ return sprintf(buffer, "%s ", tcp_conntrack_names[state]); } -static unsigned int get_conntrack_index(const struct tcphdr *tcph) +static inline 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 TCP_RST_SET; + else if (tcph->syn) return (tcph->ack ? TCP_SYNACK_SET : TCP_SYN_SET); + else if (tcph->fin) return TCP_FIN_SET; + else if (tcph->ack) return TCP_ACK_SET; + else return TCP_NONE_SET; +} + +/* 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 and the conditions: + + 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(sender) \ + ((sender)->td_maxwin > MAXACKWINCONST ? (sender)->td_maxwin : MAXACKWINCONST) + +/* + * Simplified tcp_parse_options routine from tcp_input.c + */ +static void tcp_options(struct iphdr *iph, size_t len, + struct tcphdr *tcph, + struct ip_ct_tcp_state *state) +{ + unsigned char *ptr; + int length = (tcph->doff*4) - sizeof(struct tcphdr); + + ptr = (unsigned char *)(tcph + 1); + state->td_scale = + state->flags = 0; + + while (length > 0) { + int opcode=*ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return; + case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ + length--; + continue; + default: + opsize=*ptr++; + if (opsize < 2) /* "silly options" */ + return; + if (opsize > length) + break; /* don't parse partial options */ + + if (opcode == TCPOPT_WINDOW && opsize == TCPOLEN_WINDOW) { + state->td_scale = *(u_int8_t *)ptr; + + if (state->td_scale > 14) { + /* See RFC1323 for an explanation of the limit to 14 */ + state->td_scale = 14; + } + state->flags |= IP_CT_TCP_STATE_FLAG_WINDOW_SCALE; + + return; + } + ptr += opsize - 2; + length -= opsize; + } + } +} + +static int tcp_in_window(struct ip_ct_tcp *state, + enum ip_conntrack_dir dir, + struct iphdr *iph, size_t len, + struct tcphdr *tcph) +{ + struct ip_ct_tcp_state *sender = &state->seen[dir]; + struct ip_ct_tcp_state *receiver = &state->seen[!dir]; + __u32 seq, ack, end, 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: START\n"); + 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. + */ + sender->td_end = + sender->td_maxend = end; + sender->td_maxwin = (win == 0 ? 1 : win); + + tcp_options(iph, len, tcph, sender); + /* + * RFC 1323: + * Both sides must send the Window Scale option + * to enable window scaling in either direction. + */ + if (!(sender->flags & IP_CT_TCP_STATE_FLAG_WINDOW_SCALE + && receiver->flags & IP_CT_TCP_STATE_FLAG_WINDOW_SCALE)) + sender->td_scale = + receiver->td_scale = 0; + } 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; + } + } else if (state->state == TCP_CONNTRACK_SYN_SENT + && dir == IP_CT_DIR_ORIGINAL + && after(end, sender->td_end)) { + /* + * RFC 793: "if a TCP is reinitialized ... then it need + * not wait at all; it must only be sure to use sequence + * numbers larger than those recently used." + */ + sender->td_end = + sender->td_maxend = end; + sender->td_maxwin = (win == 0 ? 1 : win); + + tcp_options(iph, len, tcph, sender); + } + + 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; + + if (sender->loose) + sender->loose--; + + DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu seq=%u ack=%u win=%u end=%u trim=%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->daddr), ntohs(tcph->dest), + seq, ack, win, end, + after(end, sender->td_maxend) && before(seq, sender->td_maxend) ? sender->td_maxend : 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); + + /* Ignore data over the right edge of the receiver's window. */ + if (after(end, sender->td_maxend) && + before(seq, sender->td_maxend)) { + end = sender->td_maxend; + if (state->stored_seq == TCP_FIN_SET) + state->stored_seq = TCP_ACK_SET; + } + DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n", + before(end, sender->td_maxend + 1) + || before(seq, sender->td_maxend + 1), + after(seq, sender->td_end - receiver->td_maxwin - 1) + || after(end, sender->td_end - receiver->td_maxwin - 1), + before(ack, receiver->td_end + 1), + after(ack, receiver->td_end - MAXACKWINDOW(sender))); + + if (sender->loose || receiver->loose || + (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(sender)))) { + /* + * Take into account window scaling (RFC 1323). + */ + if (!tcph->syn) + win <<= sender->td_scale; + + /* + * Update sender data. + */ + if (sender->td_maxwin < win) + sender->td_maxwin = win; + if (after(end, sender->td_end)) + sender->td_end = end; + if (after(ack + win, receiver->td_maxend - 1)) { + receiver->td_maxend = ack + win; + if (win == 0) + receiver->td_maxend++; + } + + /* + * Check retransmissions. + */ + if (state->stored_seq == TCP_ACK_SET) { + if (state->last_dir == dir + && state->last_seq == seq + && state->last_end == end) + state->retrans++; + else { + state->last_dir = dir; + state->last_seq = seq; + state->last_end = end; + state->retrans = 0; + } + } + + res = 1; + } else { + if (NET_RATELIMIT(ip_ct_tcp_log_invalid)) + nf_log_ip((char *)iph, len, + "ip_conntrack_tcp: IGNORED: 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(sender)) ? "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; +} + +#ifdef CONFIG_IP_NF_NAT_NEEDED +/* Update sender->td_end after NAT successfully mangled the packet */ +void ip_conntrack_tcp_update(struct sk_buff *skb, + struct ip_conntrack *conntrack, + int dir) +{ + struct iphdr *iph = skb->nh.iph; + struct tcphdr *tcph = (void *)skb->nh.iph + skb->nh.iph->ihl*4; + __u32 end; +#ifdef DEBUGP_VARS + 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), skb->len, 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); +} + +EXPORT_SYMBOL(ip_conntrack_tcp_update); +#endif + +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#define TH_ECE 0x40 +#define TH_CWR 0x80 + +/* table of valid flag combinations - ECE and CWR are always valid */ +static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] = +{ + [TH_SYN] = 1, + [TH_SYN|TH_ACK] = 1, + [TH_RST] = 1, + [TH_RST|TH_ACK] = 1, + [TH_RST|TH_ACK|TH_PUSH] = 1, + [TH_FIN|TH_ACK] = 1, + [TH_ACK] = 1, + [TH_ACK|TH_PUSH] = 1, + [TH_ACK|TH_URG] = 1, + [TH_ACK|TH_URG|TH_PUSH] = 1, + [TH_FIN|TH_ACK|TH_PUSH] = 1, + [TH_FIN|TH_ACK|TH_URG] = 1, + [TH_FIN|TH_ACK|TH_URG|TH_PUSH] = 1, +}; + +/* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c. */ +static int unclean(struct iphdr *iph, size_t len) +{ + struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); + unsigned int tcplen = len - iph->ihl * 4; + u_int8_t tcpflags; + + /* Not whole TCP header? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) { + if (NET_RATELIMIT(ip_ct_tcp_log_invalid)) + nf_log_ip((char *)iph, len, + "ip_conntrack_tcp: INVALID: truncated packet "); + return 1; + } + + /* Check TCP flags. */ + tcpflags = (((u_int8_t *)tcph)[13] & ~(TH_ECE|TH_CWR)); + if (!tcp_valid_flags[tcpflags]) { + if (NET_RATELIMIT(ip_ct_tcp_log_invalid)) + nf_log_ip((char *)iph, len, + "ip_conntrack_tcp: INVALID: invalid TCP flag combination "); + return 1; + } + + /* 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))) { + if (NET_RATELIMIT(ip_ct_tcp_log_invalid)) + nf_log_ip((char *)iph, len, + "ip_conntrack_tcp: INVALID: bad TCP checksum "); + return 1; + } + + return 0; } /* Returns verdict for packet, or -1 for invalid. */ @@ -159,60 +700,130 @@ 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 - options. */ - if (len < (iph->ihl + tcph->doff) * 4) { - DEBUGP("ip_conntrack_tcp: Truncated packet.\n"); - return -1; - } - + unsigned long timeout; + unsigned int index; + + /* Do not handle unclean packets, which could cause false alarms */ + if (unclean(iph, len)) + return -NF_ACCEPT; + WRITE_LOCK(&tcp_lock); - oldtcpstate = conntrack->proto.tcp.state; - newconntrack - = tcp_conntracks - [CTINFO2DIR(ctinfo)] - [get_conntrack_index(tcph)][oldtcpstate]; - - /* Invalid */ - if (newconntrack == TCP_CONNTRACK_MAX) { + old_state = conntrack->proto.tcp.state; + dir = CTINFO2DIR(ctinfo); + index = get_conntrack_index(tcph); + new_state = tcp_conntracks[dir][index][old_state]; + + switch (new_state) { + case TCP_CONNTRACK_IGNORE: + /* Either SYN in ORIGINAL, or SYN/ACK in REPLY direction. */ + if (index == TCP_SYNACK_SET + && conntrack->proto.tcp.stored_seq == TCP_SYN_SET + && conntrack->proto.tcp.last_dir != dir + && after(ntohl(tcph->ack_seq), conntrack->proto.tcp.last_seq)) { + /* This SYN/ACK acknowledges a SYN that we earlier ignored + * as invalid. This means that the client and the server + * are both in sync, while the firewall is not. We kill + * this session and block the SYN/ACK so that the client + * cannot but retransmit its SYN and thus initiate a + * clean new session. + */ + WRITE_UNLOCK(&tcp_lock); + if (NET_RATELIMIT(ip_ct_tcp_log_invalid)) + nf_log_ip((char *)iph, len, + "ip_conntrack_tcp: INVALID: killing out of sync session "); + if (del_timer(&conntrack->timeout)) + conntrack->timeout.function((unsigned long)conntrack); + return -NF_ACCEPT; + } + conntrack->proto.tcp.stored_seq = index; + conntrack->proto.tcp.last_dir = dir; + conntrack->proto.tcp.last_seq = ntohl(tcph->seq); + + WRITE_UNLOCK(&tcp_lock); + if (NET_RATELIMIT(ip_ct_tcp_log_invalid)) + nf_log_ip((char *)iph, len, + "ip_conntrack_tcp: INVALID: invalid SYN (ignored) "); + return NF_ACCEPT; + case TCP_CONNTRACK_MAX: + /* Invalid packet */ 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; + if (NET_RATELIMIT(ip_ct_tcp_log_invalid)) + nf_log_ip((char *)iph, len, + "ip_conntrack_tcp: INVALID: invalid state "); + return -NF_ACCEPT; + case TCP_CONNTRACK_SYN_SENT: + if (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; + } + break; + case TCP_CONNTRACK_CLOSE: + if (index == TCP_RST_SET + && test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) + && conntrack->proto.tcp.stored_seq <= TCP_SYNACK_SET + && after(ntohl(tcph->ack), conntrack->proto.tcp.last_seq)) { + /* Ignore RST closing down invalid SYN we had let trough. */ + WRITE_UNLOCK(&tcp_lock); + if (NET_RATELIMIT(ip_ct_tcp_log_invalid)) + nf_log_ip((char *)iph, len, + "ip_conntrack_tcp: INVALID: invalid RST (ignored) "); + return NF_ACCEPT; + } + /* Just fall trough */ + default: + /* Keep compilers happy. */ } - 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); - - /* 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 (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) && tcph->rst) { + conntrack->proto.tcp.stored_seq = index; + if (!tcp_in_window(&conntrack->proto.tcp, dir, iph, len, tcph)) { + /* Invalid packet */ WRITE_UNLOCK(&tcp_lock); - 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); + return -NF_ACCEPT; + } + /* If FIN was trimmed off, don't change state. */ + new_state = tcp_conntracks[dir][conntrack->proto.tcp.stored_seq][old_state]; - WRITE_UNLOCK(&tcp_lock); - ip_ct_refresh(conntrack, *tcp_timeouts[newconntrack]); + 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); + + conntrack->proto.tcp.state = new_state; + timeout = conntrack->proto.tcp.retrans >= ip_ct_tcp_max_retrans + && *tcp_timeouts[new_state] > ip_ct_tcp_timeout_max_retrans ? + ip_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state]; + + if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) { + /* 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) { + WRITE_UNLOCK(&tcp_lock); + 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 a 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); + } } + WRITE_UNLOCK(&tcp_lock); + ip_ct_refresh(conntrack, timeout); return NF_ACCEPT; } @@ -221,21 +832,80 @@ 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); +#ifdef DEBUGP_VARS + struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[0]; + struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[1]; +#endif + + /* Skip unclean packets */ + if (unclean(iph, len)) + return 0; /* 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) { + /* SYN 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; + + tcp_options(iph, len, tcph, &conntrack->proto.tcp.seen[0]); + conntrack->proto.tcp.seen[1].flags = 0; + conntrack->proto.tcp.seen[0].loose = + conntrack->proto.tcp.seen[1].loose = 0; + } else if (ip_ct_tcp_loose == 0) { + /* Don't try to pick up connections. */ + return 0; + } 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; + + /* Should we assume window scaling? */ + conntrack->proto.tcp.seen[0].flags = + conntrack->proto.tcp.seen[1].flags = 0; + conntrack->proto.tcp.seen[0].loose = + conntrack->proto.tcp.seen[1].loose = ip_ct_tcp_loose; + } + + 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; + + /* tcp_packet will set them */ + conntrack->proto.tcp.state = TCP_CONNTRACK_NONE; + conntrack->proto.tcp.stored_seq = TCP_NONE_SET; + + DEBUGP("tcp_new: 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); return 1; } diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_quake3.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_quake3.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_quake3.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_quake3.c Sun Apr 18 17:33:08 2004 @@ -0,0 +1,156 @@ +/* Quake3 extension for IP connection tracking + * (C) 2002 by Filip Sneppe + * based on ip_conntrack_ftp.c and ip_conntrack_tftp.c + * + * ip_conntrack_quake3.c v0.04 2002-08-31 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Module load syntax: + * insmod ip_conntrack_quake3.o ports=port1,port2,...port + * + * please give the ports of all Quake3 master servers You wish to + * connect to. If you don't specify ports, the default will be UDP + * port 27950. + * + * Thanks to the Ethereal folks for their analysis of the Quake3 protocol. + */ + +#include +#include +#include + +#include +#include +#include +#include + +struct module *ip_conntrack_quake3 = THIS_MODULE; + +MODULE_AUTHOR("Filip Sneppe "); +MODULE_DESCRIPTION("Netfilter connection tracking module for Quake III Arena"); +MODULE_LICENSE("GPL"); + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_c = 0; +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of Quake III master servers"); +#endif + +/* Quake3 master server reply will add > 100 expectations per reply packet; when + doing lots of printk's, klogd may not be able to read /proc/kmsg fast enough */ +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +struct quake3_search quake3s_conntrack = { "****", "getserversResponse", sizeof("getserversResponse") - 1 }; + +static int quake3_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct udphdr *udph = (void *)iph + iph->ihl * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect exp; + int i; + + /* Until there's been traffic both ways, don't look in packets. note: it's UDP ! */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_IS_REPLY) { + DEBUGP("ip_conntrack_quake3: not ok ! Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } else { DEBUGP("ip_conntrack_quake3: it's ok ! Conntrackinfo = %u\n", ctinfo); } + + if (strnicmp((const char *)udph + 12, quake3s_conntrack.pattern, quake3s_conntrack.plen) == 0) { + for(i=31; /* 8 bytes UDP hdr, 4 bytes filler, 18 bytes "getserversResponse", 1 byte "\" */ + i+6 < ntohs(udph->len); + i+=7) { + DEBUGP("ip_conntrack_quake3: adding server at offset %u/%u %u.%u.%u.%u:%u\n", + i, ntohs(udph->len), + NIPQUAD( (u_int32_t) *( (u_int32_t *)( (int)udph + i ) ) ), + ntohs((__u16) *( (__u16 *)( (int)udph + i + 4 ) ) ) ); + + memset(&exp, 0, sizeof(exp)); + + exp.tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, { 0 } }, + { (u_int32_t) *((u_int32_t *)((int)udph + i)), + { .udp = { (__u16) *((__u16 *)((int)udph+i+4)) } }, + IPPROTO_UDP } } + ); + exp.mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .udp = { 0xFFFF } }, 0xFFFF }}); + exp.expectfn = NULL; + + ip_conntrack_expect_related(ct, &exp); + } + + } + + return(NF_ACCEPT); +} + +static struct ip_conntrack_helper quake3[MAX_PORTS]; +static char quake3_names[MAX_PORTS][13]; /* quake3-65535 */ + +static void fini(void) +{ + int i; + + for(i = 0 ; (i < ports_c); i++) { + DEBUGP("ip_conntrack_quake3: unregistering helper for port %d\n", + ports[i]); + ip_conntrack_helper_unregister(&quake3[i]); + } +} + +static int __init init(void) +{ + int i, ret; + char *tmpname; + + if(!ports[0]) + ports[0]=QUAKE3_MASTER_PORT; + + for(i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) { + /* Create helper structure */ + memset(&quake3[i], 0, sizeof(struct ip_conntrack_helper)); + + quake3[i].tuple.dst.protonum = IPPROTO_UDP; + quake3[i].tuple.src.u.udp.port = htons(ports[i]); + quake3[i].mask.dst.protonum = 0xFFFF; + quake3[i].mask.src.u.udp.port = 0xFFFF; + quake3[i].help = quake3_help; + quake3[i].me = THIS_MODULE; + + tmpname = &quake3_names[i][0]; + if (ports[i] == QUAKE3_MASTER_PORT) + sprintf(tmpname, "quake3"); + else + sprintf(tmpname, "quake3-%d", i); + quake3[i].name = tmpname; + + DEBUGP("ip_conntrack_quake3: registering helper for port %d\n", + ports[i]); + + ret=ip_conntrack_helper_register(&quake3[i]); + if(ret) { + fini(); + return(ret); + } + ports_c++; + } + + return(0); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c Sun Apr 18 17:33:09 2004 @@ -0,0 +1,508 @@ +/* RPC extension for IP (TCP) connection tracking, Version 2.2 + * (C) 2000 by Marcelo Barbosa Lima + * - original rpc tracking module + * - "recent" connection handling for kernel 2.3+ netfilter + * + * (C) 2001 by Rusty Russell + * - upgraded conntrack modules to oldnat api - kernel 2.4.0+ + * + * (C) 2002,2003 by Ian (Larry) Latter + * - upgraded conntrack modules to newnat api - kernel 2.4.20+ + * - extended matching to support filtering on procedures + * + * ip_conntrack_rpc_tpc.c,v 2.2 2003/01/12 18:30:00 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + ** + * Module load syntax: + * insmod ip_conntrack_rpc_tcp.o ports=port1,port2,...port + * + * Please give the ports of all RPC servers you wish to connect to. + * If you don't specify ports, the default will be port 111. + ** + * Note to all: + * + * RPCs should not be exposed to the internet - ask the Pentagon; + * + * "The unidentified crackers pleaded guilty in July to charges + * of juvenile delinquency stemming from a string of Pentagon + * network intrusions in February. + * + * The youths, going by the names TooShort and Makaveli, used + * a common server security hole to break in, according to + * Dane Jasper, owner of the California Internet service + * provider, Sonic. They used the hole, known as the 'statd' + * exploit, to attempt more than 800 break-ins, Jasper said." + * + * From: Wired News; "Pentagon Kids Kicked Off Grid" - Nov 6, 1998 + * URL: http://www.wired.com/news/politics/0,1283,16098,00.html + ** + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_n_c = 0; + +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers (TCP/UDP) of RPC portmapper servers"); +#endif + +MODULE_AUTHOR("Marcelo Barbosa Lima "); +MODULE_DESCRIPTION("RPC TCP connection tracking module"); +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP(format, args...) printk(KERN_DEBUG "ip_conntrack_rpc_tcp: " \ + format, ## args) +#else +#define DEBUGP(format, args...) +#endif + +DECLARE_RWLOCK(ipct_rpc_tcp_lock); +#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock) +#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock) +#include + +/* For future conections RPC, using client's cache bindings + * I'll use ip_conntrack_lock to lock these lists */ + +LIST_HEAD(request_p_list_tcp); + + +static void delete_request_p(unsigned long request_p_ul) +{ + struct request_p *p = (void *)request_p_ul; + + WRITE_LOCK(&ipct_rpc_tcp_lock); + LIST_DELETE(&request_p_list_tcp, p); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(p); + return; +} + + +static void req_cl(struct request_p * r) +{ + WRITE_LOCK(&ipct_rpc_tcp_lock); + del_timer(&r->timeout); + LIST_DELETE(&request_p_list_tcp, r); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(r); + return; +} + + +static void clean_request(struct list_head *list) +{ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if (list_empty(list)) + return; + + while (first != temp) { + aux = temp->next; + req_cl((struct request_p *)temp); + temp = aux; + } + req_cl((struct request_p *)temp); + return; +} + + +static void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip, + u_int16_t port) +{ + struct request_p *req_p; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp, + struct request_p *, xid, ip, port); + + if (req_p) { + /* Refresh timeout */ + if (del_timer(&req_p->timeout)) { + req_p->timeout.expires = jiffies + EXP; + add_timer(&req_p->timeout); + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + + /* Allocate new request_p */ + req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC); + if (!req_p) { + DEBUGP("can't allocate request_p\n"); + return; + } + *req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, + { { NULL, NULL }, jiffies + EXP, (unsigned long)req_p, + NULL }}); + + /* Initialize timer */ + init_timer(&req_p->timeout); + req_p->timeout.function = delete_request_p; + add_timer(&req_p->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + list_prepend(&request_p_list_tcp, req_p); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + +} + + +static int check_rpc_packet(const u_int32_t *data, + int dir, struct ip_conntrack *ct, + struct list_head request_p_list) +{ + struct request_p *req_p; + u_int32_t xid; + struct ip_conntrack_expect expect, *exp = &expect; + + /* Translstion's buffer for XDR */ + u_int16_t port_buf; + + + /* Get XID */ + xid = *data; + + /* This does sanity checking on RPC payloads, + * and permits only the RPC "get port" (3) + * in authorised procedures in client + * communications with the portmapper. + */ + + /* perform direction dependant RPC work */ + if (dir == IP_CT_DIR_ORIGINAL) { + + data += 5; + + /* Get RPC requestor */ + if (IXDR_GET_INT32(data) != 3) { + DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n"); + return NF_ACCEPT; + } + DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n"); + + data++; + + /* Jump Credentials and Verfifier */ + data += IXDR_GET_INT32(data) + 2; + data += IXDR_GET_INT32(data) + 2; + + /* Get RPC procedure */ + DEBUGP("RPC packet contains procedure request [%u]. [cont]\n", + (unsigned int)IXDR_GET_INT32(data)); + + /* Get RPC protocol and store against client parameters */ + data = data + 2; + alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip, + ct->tuplehash[dir].tuple.src.u.all); + + DEBUGP("allocated RPC req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n", + xid, IXDR_GET_INT32(data), + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + ntohs(ct->tuplehash[dir].tuple.src.u.all)); + + DEBUGP("allocated RPC request for protocol %u. [done]\n", + (unsigned int)IXDR_GET_INT32(data)); + + } else { + + /* Check for returning packet's stored counterpart */ + req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp, + struct request_p *, xid, + ct->tuplehash[!dir].tuple.src.ip, + ct->tuplehash[!dir].tuple.src.u.all); + + /* Drop unexpected packets */ + if (!req_p) { + DEBUGP("packet is not expected. [skip]\n"); + return NF_ACCEPT; + } + + /* Verifies if packet is really an RPC reply packet */ + data = data++; + if (IXDR_GET_INT32(data) != 1) { + DEBUGP("packet is not a valid RPC reply. [skip]\n"); + return NF_ACCEPT; + } + + /* Is status accept? */ + data++; + if (IXDR_GET_INT32(data)) { + DEBUGP("packet is not an RPC accept. [skip]\n"); + return NF_ACCEPT; + } + + /* Get Verifier length. Jump verifier */ + data++; + data = data + IXDR_GET_INT32(data) + 2; + + /* Is accpet status "success"? */ + if (IXDR_GET_INT32(data)) { + DEBUGP("packet is not an RPC accept status of success. [skip]\n"); + return NF_ACCEPT; + } + + /* Get server port number */ + data++; + port_buf = (u_int16_t) IXDR_GET_INT32(data); + + /* If a packet has made it this far then it deserves an + * expectation ... if port == 0, then this service is + * not going to be registered. + */ + if (port_buf) { + DEBUGP("port found: %u\n", port_buf); + + memset(&expect, 0, sizeof(expect)); + + /* Watch out, Radioactive-Man! */ + exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; + exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; + exp->mask.src.ip = 0xffffffff; + exp->mask.dst.ip = 0xffffffff; + + switch (req_p->proto) { + case IPPROTO_UDP: + exp->tuple.src.u.udp.port = 0; + exp->tuple.dst.u.udp.port = htons(port_buf); + exp->tuple.dst.protonum = IPPROTO_UDP; + exp->mask.src.u.udp.port = 0; + exp->mask.dst.u.udp.port = htons(0xffff); + exp->mask.dst.protonum = 0xffff; + break; + + case IPPROTO_TCP: + exp->tuple.src.u.tcp.port = 0; + exp->tuple.dst.u.tcp.port = htons(port_buf); + exp->tuple.dst.protonum = IPPROTO_TCP; + exp->mask.src.u.tcp.port = 0; + exp->mask.dst.u.tcp.port = htons(0xffff); + exp->mask.dst.protonum = 0xffff; + break; + } + exp->expectfn = NULL; + + ip_conntrack_expect_related(ct, &expect); + + DEBUGP("expect related ip %u.%u.%u.%u:0-%u.%u.%u.%u:%u proto=%u\n", + NIPQUAD(exp->tuple.src.ip), + NIPQUAD(exp->tuple.dst.ip), + port_buf, req_p->proto); + + DEBUGP("expect related mask %u.%u.%u.%u:0-%u.%u.%u.%u:65535 proto=%u\n", + NIPQUAD(exp->mask.src.ip), + NIPQUAD(exp->mask.dst.ip), + exp->mask.dst.protonum); + + } + + req_cl(req_p); + + DEBUGP("packet evaluated. [expect]\n"); + return NF_ACCEPT; + } + + return NF_ACCEPT; + +} + + +/* RPC TCP helper */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + struct tcphdr *tcph = (void *) iph + iph->ihl * 4; + const u_int32_t *data = (const u_int32_t *)tcph + tcph->doff; + size_t tcplen = len - iph->ihl * 4; + + int dir = CTINFO2DIR(ctinfo); + int crp_ret; + + + DEBUGP("new packet to evaluate ..\n"); + + /* This works for packets like handshake packets, ignore */ + if (len == ((tcph->doff + iph->ihl) * 4)) { + DEBUGP("packet has no data (may still be handshaking). [skip]\n"); + return NF_ACCEPT; + } + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("connection tracking state is; ctinfo=%u ..\n", ctinfo); + DEBUGP("[note: failure to get past this error may indicate asymmetric routing]\n"); + DEBUGP("packet is not yet part of a two way stream. [skip]\n"); + return NF_ACCEPT; + } + + /* Not whole TCP header? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { + DEBUGP("TCP header length is; tcplen=%u ..\n", (unsigned) tcplen); + DEBUGP("packet does not contain a complete TCP header. [skip]\n"); + return NF_ACCEPT; + } + + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *) tcph, tcplen, 0))) { + DEBUGP("csum; %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + DEBUGP("[note: failure to get past this error may indicate source routing]\n"); + DEBUGP("packet contains a bad checksum. [skip]\n"); + return NF_ACCEPT; + } + + /* perform direction dependant protocol work */ + if (dir == IP_CT_DIR_ORIGINAL) { + + DEBUGP("packet is from the initiator. [cont]\n"); + + /* Tests if packet len is ok */ + if ((tcplen - (tcph->doff * 4)) != 60) { + DEBUGP("packet length is not correct. [skip]\n"); + return NF_ACCEPT; + } + + } else { + + DEBUGP("packet is from the receiver. [cont]\n"); + + /* Tests if packet len is ok */ + if ((tcplen - (tcph->doff * 4)) != 32) { + DEBUGP("packet length is not correct. [skip]\n"); + return NF_ACCEPT; + } + } + + /* Get to the data */ + data++; + + /* Check the RPC data */ + crp_ret = check_rpc_packet(data, dir, ct, request_p_list_tcp); + + return crp_ret; + +} + + +static struct ip_conntrack_helper rpc_helpers[MAX_PORTS]; + +static void fini(void); + + +static int __init init(void) +{ + int port, ret; + static char name[10]; + + + /* If no port given, default to standard RPC port */ + if (ports[0] == 0) + ports[0] = RPC_PORT; + + for (port = 0; (port < MAX_PORTS) && ports[port]; port++) { + memset(&rpc_helpers[port], 0, sizeof(struct ip_conntrack_helper)); + + if (ports[port] == RPC_PORT) + sprintf(name, "rpc"); + else + sprintf(name, "rpc-%d", port); + + rpc_helpers[port].name = name; + rpc_helpers[port].me = THIS_MODULE; + rpc_helpers[port].max_expected = 1; + rpc_helpers[port].flags = IP_CT_HELPER_F_REUSE_EXPECT; + rpc_helpers[port].timeout = 0; + + rpc_helpers[port].tuple.dst.protonum = IPPROTO_TCP; + rpc_helpers[port].mask.dst.protonum = 0xffff; + + /* RPC can come from ports 0:65535 to ports[port] (111) */ + rpc_helpers[port].tuple.src.u.udp.port = htons(ports[port]); + rpc_helpers[port].mask.src.u.udp.port = htons(0xffff); + rpc_helpers[port].mask.dst.u.udp.port = htons(0x0); + + rpc_helpers[port].help = help; + + DEBUGP("registering helper for port #%d: %d/TCP\n", port, ports[port]); + DEBUGP("helper match ip %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(rpc_helpers[port].tuple.dst.ip), + ntohs(rpc_helpers[port].tuple.dst.u.tcp.port), + NIPQUAD(rpc_helpers[port].tuple.src.ip), + ntohs(rpc_helpers[port].tuple.src.u.tcp.port)); + DEBUGP("helper match mask %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(rpc_helpers[port].mask.dst.ip), + ntohs(rpc_helpers[port].mask.dst.u.tcp.port), + NIPQUAD(rpc_helpers[port].mask.src.ip), + ntohs(rpc_helpers[port].mask.src.u.tcp.port)); + + ret = ip_conntrack_helper_register(&rpc_helpers[port]); + + if (ret) { + printk("ERROR registering port %d\n", + ports[port]); + fini(); + return -EBUSY; + } + ports_n_c++; + } + return 0; +} + + +/* This function is intentionally _NOT_ defined as __exit, because + * it is needed by the init function */ +static void fini(void) +{ + int port; + + DEBUGP("cleaning request list\n"); + clean_request(&request_p_list_tcp); + + for (port = 0; (port < ports_n_c) && ports[port]; port++) { + DEBUGP("unregistering port %d\n", ports[port]); + ip_conntrack_helper_unregister(&rpc_helpers[port]); + } +} + + +module_init(init); +module_exit(fini); + +struct module *ip_conntrack_rpc_tcp = THIS_MODULE; +EXPORT_SYMBOL(request_p_list_tcp); +EXPORT_SYMBOL(ip_conntrack_rpc_tcp); +EXPORT_SYMBOL(ipct_rpc_tcp_lock); + diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_rpc_udp.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_rpc_udp.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_rpc_udp.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_rpc_udp.c Sun Apr 18 17:33:09 2004 @@ -0,0 +1,503 @@ +/* RPC extension for IP (UDP) connection tracking, Version 2.2 + * (C) 2000 by Marcelo Barbosa Lima + * - original rpc tracking module + * - "recent" connection handling for kernel 2.3+ netfilter + * + * (C) 2001 by Rusty Russell + * - upgraded conntrack modules to oldnat api - kernel 2.4.0+ + * + * (C) 2002,2003 by Ian (Larry) Latter + * - upgraded conntrack modules to newnat api - kernel 2.4.20+ + * - extended matching to support filtering on procedures + * + * ip_conntrack_rpc_udp.c,v 2.2 2003/01/12 18:30:00 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + ** + * Module load syntax: + * insmod ip_conntrack_rpc_udp.o ports=port1,port2,...port + * + * Please give the ports of all RPC servers you wish to connect to. + * If you don't specify ports, the default will be port 111. + ** + * Note to all: + * + * RPCs should not be exposed to the internet - ask the Pentagon; + * + * "The unidentified crackers pleaded guilty in July to charges + * of juvenile delinquency stemming from a string of Pentagon + * network intrusions in February. + * + * The youths, going by the names TooShort and Makaveli, used + * a common server security hole to break in, according to + * Dane Jasper, owner of the California Internet service + * provider, Sonic. They used the hole, known as the 'statd' + * exploit, to attempt more than 800 break-ins, Jasper said." + * + * From: Wired News; "Pentagon Kids Kicked Off Grid" - Nov 6, 1998 + * URL: http://www.wired.com/news/politics/0,1283,16098,00.html + ** + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_n_c = 0; + +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers (TCP/UDP) of RPC portmapper servers"); +#endif + +MODULE_AUTHOR("Marcelo Barbosa Lima "); +MODULE_DESCRIPTION("RPC UDP connection tracking module"); +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP(format, args...) printk(KERN_DEBUG "ip_conntrack_rpc_udp: " \ + format, ## args) +#else +#define DEBUGP(format, args...) +#endif + +DECLARE_RWLOCK(ipct_rpc_udp_lock); +#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock) +#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock) +#include + +/* For future conections RPC, using client's cache bindings + * I'll use ip_conntrack_lock to lock these lists */ + +LIST_HEAD(request_p_list_udp); + + +static void delete_request_p(unsigned long request_p_ul) +{ + struct request_p *p = (void *)request_p_ul; + + WRITE_LOCK(&ipct_rpc_udp_lock); + LIST_DELETE(&request_p_list_udp, p); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(p); + return; +} + + +static void req_cl(struct request_p * r) +{ + WRITE_LOCK(&ipct_rpc_udp_lock); + del_timer(&r->timeout); + LIST_DELETE(&request_p_list_udp, r); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(r); + return; +} + + +static void clean_request(struct list_head *list) +{ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if (list_empty(list)) + return; + + while (first != temp) { + aux = temp->next; + req_cl((struct request_p *)temp); + temp = aux; + } + req_cl((struct request_p *)temp); + return; +} + + +static void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip, + u_int16_t port) +{ + struct request_p *req_p; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_udp_lock); + req_p = LIST_FIND(&request_p_list_udp, request_p_cmp, + struct request_p *, xid, ip, port); + + if (req_p) { + /* Refresh timeout */ + if (del_timer(&req_p->timeout)) { + req_p->timeout.expires = jiffies + EXP; + add_timer(&req_p->timeout); + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + + /* Allocate new request_p */ + req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC); + if (!req_p) { + DEBUGP("can't allocate request_p\n"); + return; + } + *req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, + { { NULL, NULL }, jiffies + EXP, (unsigned long)req_p, + NULL }}); + + /* Initialize timer */ + init_timer(&req_p->timeout); + req_p->timeout.function = delete_request_p; + add_timer(&req_p->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_udp_lock); + list_prepend(&request_p_list_udp, req_p); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + +} + + +static int check_rpc_packet(const u_int32_t *data, + int dir, struct ip_conntrack *ct, + struct list_head request_p_list) +{ + struct request_p *req_p; + u_int32_t xid; + struct ip_conntrack_expect expect, *exp = &expect; + + /* Translstion's buffer for XDR */ + u_int16_t port_buf; + + + /* Get XID */ + xid = *data; + + /* This does sanity checking on RPC payloads, + * and permits only the RPC "get port" (3) + * in authorised procedures in client + * communications with the portmapper. + */ + + /* perform direction dependant RPC work */ + if (dir == IP_CT_DIR_ORIGINAL) { + + data += 5; + + /* Get RPC requestor */ + if (IXDR_GET_INT32(data) != 3) { + DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n"); + return NF_ACCEPT; + } + DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n"); + + data++; + + /* Jump Credentials and Verfifier */ + data = data + IXDR_GET_INT32(data) + 2; + data = data + IXDR_GET_INT32(data) + 2; + + /* Get RPC procedure */ + DEBUGP("RPC packet contains procedure request [%u]. [cont]\n", + (unsigned int)IXDR_GET_INT32(data)); + + /* Get RPC protocol and store against client parameters */ + data = data + 2; + alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip, + ct->tuplehash[dir].tuple.src.u.all); + + DEBUGP("allocated RPC req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n", + xid, IXDR_GET_INT32(data), + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + ntohs(ct->tuplehash[dir].tuple.src.u.all)); + + DEBUGP("allocated RPC request for protocol %u. [done]\n", + (unsigned int)IXDR_GET_INT32(data)); + + } else { + + /* Check for returning packet's stored counterpart */ + req_p = LIST_FIND(&request_p_list_udp, request_p_cmp, + struct request_p *, xid, + ct->tuplehash[!dir].tuple.src.ip, + ct->tuplehash[!dir].tuple.src.u.all); + + /* Drop unexpected packets */ + if (!req_p) { + DEBUGP("packet is not expected. [skip]\n"); + return NF_ACCEPT; + } + + /* Verifies if packet is really an RPC reply packet */ + data = data++; + if (IXDR_GET_INT32(data) != 1) { + DEBUGP("packet is not a valid RPC reply. [skip]\n"); + return NF_ACCEPT; + } + + /* Is status accept? */ + data++; + if (IXDR_GET_INT32(data)) { + DEBUGP("packet is not an RPC accept. [skip]\n"); + return NF_ACCEPT; + } + + /* Get Verifier length. Jump verifier */ + data++; + data = data + IXDR_GET_INT32(data) + 2; + + /* Is accpet status "success"? */ + if (IXDR_GET_INT32(data)) { + DEBUGP("packet is not an RPC accept status of success. [skip]\n"); + return NF_ACCEPT; + } + + /* Get server port number */ + data++; + port_buf = (u_int16_t) IXDR_GET_INT32(data); + + /* If a packet has made it this far then it deserves an + * expectation ... if port == 0, then this service is + * not going to be registered. + */ + if (port_buf) { + DEBUGP("port found: %u\n", port_buf); + + memset(&expect, 0, sizeof(expect)); + + /* Watch out, Radioactive-Man! */ + exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; + exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; + exp->mask.src.ip = 0xffffffff; + exp->mask.dst.ip = 0xffffffff; + + switch (req_p->proto) { + case IPPROTO_UDP: + exp->tuple.src.u.udp.port = 0; + exp->tuple.dst.u.udp.port = htons(port_buf); + exp->tuple.dst.protonum = IPPROTO_UDP; + exp->mask.src.u.udp.port = 0; + exp->mask.dst.u.udp.port = htons(0xffff); + exp->mask.dst.protonum = 0xffff; + break; + + case IPPROTO_TCP: + exp->tuple.src.u.tcp.port = 0; + exp->tuple.dst.u.tcp.port = htons(port_buf); + exp->tuple.dst.protonum = IPPROTO_TCP; + exp->mask.src.u.tcp.port = 0; + exp->mask.dst.u.tcp.port = htons(0xffff); + exp->mask.dst.protonum = 0xffff; + break; + } + exp->expectfn = NULL; + + ip_conntrack_expect_related(ct, &expect); + + DEBUGP("expect related ip %u.%u.%u.%u:0-%u.%u.%u.%u:%u proto=%u\n", + NIPQUAD(exp->tuple.src.ip), + NIPQUAD(exp->tuple.dst.ip), + port_buf, req_p->proto); + + DEBUGP("expect related mask %u.%u.%u.%u:0-%u.%u.%u.%u:65535 proto=%u\n", + NIPQUAD(exp->mask.src.ip), + NIPQUAD(exp->mask.dst.ip), + exp->mask.dst.protonum); + + } + + req_cl(req_p); + + DEBUGP("packet evaluated. [expect]\n"); + return NF_ACCEPT; + } + + return NF_ACCEPT; + +} + + +/* RPC UDP helper */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + struct udphdr *udph = (void *) iph + iph->ihl * 4; + const u_int32_t *data = (const u_int32_t *)udph + 2; + size_t udplen = len - iph->ihl * 4; + int dir = CTINFO2DIR(ctinfo); + int crp_ret; + + /* Checksum */ + const u_int16_t *chsm = (const u_int16_t *)udph + 3; + + + DEBUGP("new packet to evaluate ..\n"); + + /* Not whole UDP header? */ + if (udplen < sizeof(struct udphdr)) { + DEBUGP("UDP header length is; udplen=%u ..\n", (unsigned) udplen); + DEBUGP("packet does not contain a complete UDP header. [skip]\n"); + return NF_ACCEPT; + } + + /* FIXME: Source route IP option packets --RR */ + if (*chsm) { + if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + csum_partial((char *)udph, udplen, 0))) { + DEBUGP("[note: failure to get past this error may indicate source routing]\n"); + DEBUGP("packet contains a bad checksum. [skip]\n"); + return NF_ACCEPT; + } + } + + /* perform direction dependant protocol work */ + if (dir == IP_CT_DIR_ORIGINAL) { + + DEBUGP("packet is from the initiator. [cont]\n"); + + /* Tests if packet len is ok */ + if ((udplen - sizeof(struct udphdr)) != 56) { + DEBUGP("packet length is not correct. [skip]\n"); + return NF_ACCEPT; + } + + } else { + + DEBUGP("packet is from the receiver. [cont]\n"); + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("connection tracking state is; ctinfo=%u ..\n", ctinfo); + DEBUGP("[note: failure to get past this error may indicate asymmetric routing]\n"); + DEBUGP("packet is not yet part of a two way stream. [skip]\n"); + return NF_ACCEPT; + } + + /* Tests if packet len is ok */ + if ((udplen - sizeof(struct udphdr)) != 28) { + DEBUGP("packet length is not correct. [skip]\n"); + return NF_ACCEPT; + } + + } + + /* Get to the data */ + /* udp *data == *correct */ + + /* Check the RPC data */ + crp_ret = check_rpc_packet(data, dir, ct, request_p_list_udp); + + return crp_ret; + +} + + +static struct ip_conntrack_helper rpc_helpers[MAX_PORTS]; + +static void fini(void); + + +static int __init init(void) +{ + int port, ret; + static char name[10]; + + + /* If no port given, default to standard RPC port */ + if (ports[0] == 0) + ports[0] = RPC_PORT; + + for (port = 0; (port < MAX_PORTS) && ports[port]; port++) { + memset(&rpc_helpers[port], 0, sizeof(struct ip_conntrack_helper)); + + if (ports[port] == RPC_PORT) + sprintf(name, "rpc"); + else + sprintf(name, "rpc-%d", port); + + rpc_helpers[port].name = name; + rpc_helpers[port].me = THIS_MODULE; + rpc_helpers[port].max_expected = 1; + rpc_helpers[port].flags = IP_CT_HELPER_F_REUSE_EXPECT; + rpc_helpers[port].timeout = 0; + + rpc_helpers[port].tuple.dst.protonum = IPPROTO_UDP; + rpc_helpers[port].mask.dst.protonum = 0xffff; + + /* RPC can come from ports 0:65535 to ports[port] (111) */ + rpc_helpers[port].tuple.src.u.udp.port = htons(ports[port]); + rpc_helpers[port].mask.src.u.udp.port = htons(0xffff); + rpc_helpers[port].mask.dst.u.udp.port = htons(0x0); + + rpc_helpers[port].help = help; + + DEBUGP("registering helper for port #%d: %d/UDP\n", port, ports[port]); + DEBUGP("helper match ip %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(rpc_helpers[port].tuple.dst.ip), + ntohs(rpc_helpers[port].tuple.dst.u.udp.port), + NIPQUAD(rpc_helpers[port].tuple.src.ip), + ntohs(rpc_helpers[port].tuple.src.u.udp.port)); + DEBUGP("helper match mask %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(rpc_helpers[port].mask.dst.ip), + ntohs(rpc_helpers[port].mask.dst.u.udp.port), + NIPQUAD(rpc_helpers[port].mask.src.ip), + ntohs(rpc_helpers[port].mask.src.u.udp.port)); + + ret = ip_conntrack_helper_register(&rpc_helpers[port]); + + if (ret) { + printk("ERROR registering port %d\n", + ports[port]); + fini(); + return -EBUSY; + } + ports_n_c++; + } + return 0; +} + + +/* This function is intentionally _NOT_ defined as __exit, because + * it is needed by the init function */ +static void fini(void) +{ + int port; + + DEBUGP("cleaning request list\n"); + clean_request(&request_p_list_udp); + + for (port = 0; (port < ports_n_c) && ports[port]; port++) { + DEBUGP("unregistering port %d\n", ports[port]); + ip_conntrack_helper_unregister(&rpc_helpers[port]); + } +} + + +module_init(init); +module_exit(fini); + +struct module *ip_conntrack_rpc_udp = THIS_MODULE; +EXPORT_SYMBOL(request_p_list_udp); +EXPORT_SYMBOL(ip_conntrack_rpc_udp); +EXPORT_SYMBOL(ipct_rpc_udp_lock); + diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_rsh.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_rsh.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_rsh.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_rsh.c Sun Apr 18 17:33:10 2004 @@ -0,0 +1,331 @@ +/* RSH extension for IP connection tracking, Version 1.0 + * (C) 2002 by Ian (Larry) Latter + * based on HW's ip_conntrack_irc.c + * + * ip_conntrack_rsh.c,v 1.0 2002/07/17 14:49:26 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + ** + * Module load syntax: + * insmod ip_conntrack_rsh.o ports=port1,port2,...port + * + * please give the ports of all RSH servers You wish to connect to. + * If You don't specify ports, the default will be port 514 + ** + * Note to all: + * RSH blows ... you should use SSH (openssh.org) to replace it, + * unfortunately I babysit some sysadmins that won't migrate + * their legacy crap, in our second tier. + */ + + +/* + * Some docco ripped from the net to teach me all there is to know about + * RSH, in 16.5 seconds (ie, all of the non-netfilter docco used to write + * this module). + * + * I have no idea what "unix rshd man pages" these guys have .. but that + * is some pretty detailed docco! + ** + * + * 4. Of the rsh protocol. + * ----------------------- + * + * The rshd listens on TCP port #514. The following info is from the unix + * rshd man pages : + * + * "Service Request Protocol + * + * When the rshd daemon receives a service request, it initiates the + * following protocol: + * + * 1. The rshd daemon checks the source port number for the request. + * If the port number is not in the range 0 through 1023, the rshd daemon + * terminates the connection. + * + * 2. The rshd daemon reads characters from the socket up to a null byte. + * The string read is interpreted as an ASCII number (base 10). If this + * number is nonzero, the rshd daemon interprets it as the port number + * of a secondary stream to be used as standard error. A second connection + * is created to the specified port on the client host. The source port + * on the local host is in the range 0 through 1023. + * + * 3. The rshd daemon uses the source address of the initial connection + * request to determine the name of the client host. If the name cannot + * be determined, the rshd daemon uses the dotted decimal representation + * of the client host's address. + * + * 4. The rshd daemon retrieves the following information from the initial + * socket: + * + * * A null-terminated string of at most 16 bytes interpreted as + * the user name of the user on the client host. + * + * * A null-terminated string of at most 16 bytes interpreted as + * the user name to be used on the local server host. + * + * * Another null-terminated string interpreted as a command line + * to be passed to a shell on the local server host. + * + * 5. The rshd daemon attempts to validate the user using the following steps: + * + * a. The rshd daemon looks up the local user name in the /etc/passwd + * file and tries to switch to the home directory (using the chdir + * subroutine). If either the lookup or the directory change fails, + * the rshd daemon terminates the connection. + * + * b. If the local user ID is a nonzero value, the rshd daemon searches + * the /etc/hosts.equiv file to see if the name of the client + * workstation is listed. If the client workstation is listed as an + * equivalent host, the rshd daemon validates the user. + * + * c. If the $HOME/.rhosts file exists, the rshd daemon tries to + * authenticate the user by checking the .rhosts file. + * + * d. If either the $HOME/.rhosts authentication fails or the + * client host is not an equivalent host, the rshd daemon + * terminates the connection. + * + * 6. Once rshd validates the user, the rshd daemon returns a null byte + * on the initial connection and passes the command line to the user's + * local login shell. The shell then inherits the network connections + * established by the rshd daemon." + * + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_n_c = 0; + +MODULE_AUTHOR("Ian (Larry) Latter "); +MODULE_DESCRIPTION("RSH connection tracking module"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of RSH servers"); +#endif + +DECLARE_LOCK(ip_rsh_lock); +struct module *ip_conntrack_rsh = THIS_MODULE; + +#if 0 +#define DEBUGP(format, args...) printk(KERN_DEBUG "ip_conntrack_rsh: " \ + format, ## args) +#else +#define DEBUGP(format, args...) +#endif + + + +/* FIXME: This should be in userspace. Later. */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + /* tcplen not negative guarenteed by ip_conntrack_tcp.c */ + struct tcphdr *tcph = (void *) iph + iph->ihl * 4; + const char *data = (const char *) tcph + tcph->doff * 4; + u_int32_t tcplen = len - iph->ihl * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_rsh_expect *exp_rsh_info = &exp->help.exp_rsh_info; + u_int16_t port; + int maxoctet; + + /* note that "maxoctet" is used to maintain sanity (8 was the + * original array size used in rshd/glibc) -- is there a + * vulnerability in rshd.c in the looped port *= 10? + */ + + + DEBUGP("entered\n"); + + /* bail if packet is not from RSH client */ + if (dir == IP_CT_DIR_REPLY) + return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { + DEBUGP("tcplen = %u\n", (unsigned) tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *) tcph, tcplen, 0))) { + DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + /* find the rsh stderr port */ + maxoctet = 4; + port = 0; + for ( ; *data != 0 && maxoctet != 0; data++, maxoctet--) { + if (*data < 0) + return(1); + if (*data == 0) + break; + if (*data < 48 || *data > 57) { + DEBUGP("these aren't the packets you're looking for ..\n"); + return NF_ACCEPT; + } + port = port * 10 + ( *data - 48 ); + } + + /* dont relate sessions that try to expose the client */ + DEBUGP("found port %u\n", port); + if (port > 1023) { + DEBUGP("skipping, expected port size is greater than 1023!\n"); + return NF_ACCEPT; + } + + + LOCK_BH(&ip_rsh_lock); + + /* new(,related) connection is; + * reply + dst (uint)port + src port (0:1023) + */ + memset(&expect, 0, sizeof(expect)); + + /* save some discovered data, in case someone ever wants to write + * a NAT module for this bastard .. + */ + exp_rsh_info->port = port; + + DEBUGP("wrote info port=%u\n", exp_rsh_info->port); + + + /* Watch out, Radioactive-Man! */ + exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; + exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; + exp->tuple.src.u.tcp.port = 0; + exp->tuple.dst.u.tcp.port = htons(exp_rsh_info->port); + exp->tuple.dst.protonum = IPPROTO_TCP; + + exp->mask.src.ip = 0xffffffff; + exp->mask.dst.ip = 0xffffffff; + + exp->mask.src.u.tcp.port = htons(0xfc00); + exp->mask.dst.u.tcp.port = htons(0xfc00); + exp->mask.dst.protonum = 0xffff; + + exp->expectfn = NULL; + + ip_conntrack_expect_related(ct, &expect); + + DEBUGP("expect related ip %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", + NIPQUAD(exp->tuple.src.ip), + ntohs(exp->tuple.src.u.tcp.port), + NIPQUAD(exp->tuple.dst.ip), + ntohs(exp->tuple.dst.u.tcp.port)); + + DEBUGP("expect related mask %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", + NIPQUAD(exp->mask.src.ip), + ntohs(exp->mask.src.u.tcp.port), + NIPQUAD(exp->mask.dst.ip), + ntohs(exp->mask.dst.u.tcp.port)); + UNLOCK_BH(&ip_rsh_lock); + + return NF_ACCEPT; +} + +static struct ip_conntrack_helper rsh_helpers[MAX_PORTS]; + +static void fini(void); + +static int __init init(void) +{ + int port, ret; + static char name[10]; + + + /* If no port given, default to standard RSH port */ + if (ports[0] == 0) + ports[0] = RSH_PORT; + + for (port = 0; (port < MAX_PORTS) && ports[port]; port++) { + memset(&rsh_helpers[port], 0, sizeof(struct ip_conntrack_helper)); + + if (ports[port] == RSH_PORT) + sprintf(name, "rsh"); + else + sprintf(name, "rsh-%d", port); + + rsh_helpers[port].name = name; + rsh_helpers[port].me = THIS_MODULE; + rsh_helpers[port].max_expected = 1; + rsh_helpers[port].flags = IP_CT_HELPER_F_REUSE_EXPECT; + rsh_helpers[port].timeout = 0; + + rsh_helpers[port].tuple.dst.protonum = IPPROTO_TCP; + rsh_helpers[port].mask.dst.protonum = 0xffff; + + /* RSH must come from ports 0:1023 to ports[port] (514) */ + rsh_helpers[port].tuple.src.u.tcp.port = htons(ports[port]); + rsh_helpers[port].mask.src.u.tcp.port = htons(0xfc00); + rsh_helpers[port].mask.dst.u.tcp.port = htons(0xfc00); + + rsh_helpers[port].help = help; + + DEBUGP("registering helper for port #%d: %d/TCP\n", port, ports[port]); + DEBUGP("helper match ip %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", + NIPQUAD(rsh_helpers[port].tuple.src.ip), + ntohs(rsh_helpers[port].tuple.src.u.tcp.port), + NIPQUAD(rsh_helpers[port].tuple.dst.ip), + ntohs(rsh_helpers[port].tuple.dst.u.tcp.port)); + DEBUGP("helper match mask %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", + NIPQUAD(rsh_helpers[port].mask.src.ip), + ntohs(rsh_helpers[port].mask.src.u.tcp.port), + NIPQUAD(rsh_helpers[port].mask.dst.ip), + ntohs(rsh_helpers[port].mask.dst.u.tcp.port)); + + ret = ip_conntrack_helper_register(&rsh_helpers[port]); + + if (ret) { + printk("ERROR registering port %d\n", + ports[port]); + fini(); + return -EBUSY; + } + ports_n_c++; + } + return 0; +} + +/* This function is intentionally _NOT_ defined as __exit, because + * it is needed by the init function */ +static void fini(void) +{ + int port; + for (port = 0; (port < MAX_PORTS) && ports[port]; port++) { + DEBUGP("unregistering port %d\n", ports[port]); + ip_conntrack_helper_unregister(&rsh_helpers[port]); + } +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_rtsp.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_rtsp.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_rtsp.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_rtsp.c Sun Apr 18 17:33:10 2004 @@ -0,0 +1,509 @@ +/* + * RTSP extension for IP connection tracking + * (C) 2003 by Tom Marshall + * based on ip_conntrack_irc.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Module load syntax: + * insmod ip_conntrack_rtsp.o ports=port1,port2,...port + * max_outstanding=n setup_timeout=secs + * + * If no ports are specified, the default will be port 554. + * + * With max_outstanding you can define the maximum number of not yet + * answered SETUP requests per RTSP session (default 8). + * With setup_timeout you can specify how long the system waits for + * an expected data channel (default 300 seconds). + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#define NF_NEED_STRNCASECMP +#define NF_NEED_STRTOU16 +#define NF_NEED_STRTOU32 +#define NF_NEED_NEXTLINE +#include +#define NF_NEED_MIME_NEXTLINE +#include + +#define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */ + +#define INFOP(args...) printk(KERN_INFO __FILE__ ":" __FUNCTION__ ":" args) +#ifdef IP_NF_RTSP_DEBUG +#define DEBUGP(args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ ":" args) +#else +#define DEBUGP(args...) +#endif + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int num_ports = 0; +static int max_outstanding = 8; +static unsigned int setup_timeout = 300; + +MODULE_AUTHOR("Tom Marshall "); +MODULE_DESCRIPTION("RTSP 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 RTSP servers"); +MODULE_PARM(max_outstanding, "i"); +MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP requests per RTSP session"); +MODULE_PARM(setup_timeout, "i"); +MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels"); +#endif + +DECLARE_LOCK(ip_rtsp_lock); +struct module* ip_conntrack_rtsp = THIS_MODULE; + +/* + * Max mappings we will allow for one RTSP connection (for RTP, the number + * of allocated ports is twice this value). Note that SMIL burns a lot of + * ports so keep this reasonably high. If this is too low, you will see a + * lot of "no free client map entries" messages. + */ +#define MAX_PORT_MAPS 16 + +/*** default port list was here in the masq code: 554, 3030, 4040 ***/ + +#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; } + +/* + * Parse an RTSP packet. + * + * Returns zero if parsing failed. + * + * Parameters: + * IN ptcp tcp data pointer + * IN tcplen tcp data len + * IN/OUT ptcpoff points to current tcp offset + * OUT phdrsoff set to offset of rtsp headers + * OUT phdrslen set to length of rtsp headers + * OUT pcseqoff set to offset of CSeq header + * OUT pcseqlen set to length of CSeq header + */ +static int +rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff, + uint* phdrsoff, uint* phdrslen, + uint* pcseqoff, uint* pcseqlen) +{ + uint entitylen = 0; + uint lineoff; + uint linelen; + + if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) + { + return 0; + } + + *phdrsoff = *ptcpoff; + while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) + { + if (linelen == 0) + { + if (entitylen > 0) + { + *ptcpoff += min(entitylen, tcplen - *ptcpoff); + } + break; + } + if (lineoff+linelen > tcplen) + { + INFOP("!! overrun !!\n"); + break; + } + + if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) + { + *pcseqoff = lineoff; + *pcseqlen = linelen; + } + if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) + { + uint off = lineoff+15; + SKIP_WSPACE(ptcp+lineoff, linelen, off); + nf_strtou32(ptcp+off, &entitylen); + } + } + *phdrslen = (*ptcpoff) - (*phdrsoff); + + return 1; +} + +/* + * Find lo/hi client ports (if any) in transport header + * In: + * ptcp, tcplen = packet + * tranoff, tranlen = buffer to search + * + * Out: + * pport_lo, pport_hi = lo/hi ports (host endian) + * + * Returns nonzero if any client ports found + * + * Note: it is valid (and expected) for the client to request multiple + * transports, so we need to parse the entire line. + */ +static int +rtsp_parse_transport(char* ptran, uint tranlen, + struct ip_ct_rtsp_expect* prtspexp) +{ + int rc = 0; + uint off = 0; + + if (tranlen < 10 || !iseol(ptran[tranlen-1]) || + nf_strncasecmp(ptran, "Transport:", 10) != 0) + { + INFOP("sanity check failed\n"); + return 0; + } + DEBUGP("tran='%.*s'\n", (int)tranlen, ptran); + off += 10; + SKIP_WSPACE(ptran, tranlen, off); + + /* Transport: tran;field;field=val,tran;field;field=val,... */ + while (off < tranlen) + { + const char* pparamend; + uint nextparamoff; + + pparamend = memchr(ptran+off, ',', tranlen-off); + pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; + nextparamoff = pparamend-ptran; + + while (off < nextparamoff) + { + const char* pfieldend; + uint nextfieldoff; + + pfieldend = memchr(ptran+off, ';', nextparamoff-off); + nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; + + if (strncmp(ptran+off, "client_port=", 12) == 0) + { + u_int16_t port; + uint numlen; + + off += 12; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + if (prtspexp->loport != 0 && prtspexp->loport != port) + { + DEBUGP("multiple ports found, port %hu ignored\n", port); + } + else + { + prtspexp->loport = prtspexp->hiport = port; + if (ptran[off] == '-') + { + off++; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + prtspexp->pbtype = pb_range; + prtspexp->hiport = port; + + // If we have a range, assume rtp: + // loport must be even, hiport must be loport+1 + if ((prtspexp->loport & 0x0001) != 0 || + prtspexp->hiport != prtspexp->loport+1) + { + DEBUGP("incorrect range: %hu-%hu, correcting\n", + prtspexp->loport, prtspexp->hiport); + prtspexp->loport &= 0xfffe; + prtspexp->hiport = prtspexp->loport+1; + } + } + else if (ptran[off] == '/') + { + off++; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + prtspexp->pbtype = pb_discon; + prtspexp->hiport = port; + } + rc = 1; + } + } + + /* + * Note we don't look for the destination parameter here. + * If we are using NAT, the NAT module will handle it. If not, + * and the client is sending packets elsewhere, the expectation + * will quietly time out. + */ + + off = nextfieldoff; + } + + off = nextparamoff; + } + + return rc; +} + +/*** conntrack functions ***/ + +/* outbound packet: client->server */ +static int +help_out(const struct iphdr* iph, size_t pktlen, + struct ip_conntrack* ct, enum ip_conntrack_info ctinfo) +{ + int dir = CTINFO2DIR(ctinfo); /* = IP_CT_DIR_ORIGINAL */ + struct tcphdr* tcph = (void*)iph + iph->ihl * 4; + uint tcplen = pktlen - iph->ihl * 4; + char* pdata = (char*)tcph + tcph->doff * 4; + uint datalen = tcplen - tcph->doff * 4; + uint dataoff = 0; + + struct ip_conntrack_expect exp; + + while (dataoff < datalen) + { + uint cmdoff = dataoff; + uint hdrsoff = 0; + uint hdrslen = 0; + uint cseqoff = 0; + uint cseqlen = 0; + uint lineoff = 0; + uint linelen = 0; + uint off; + int rc; + + if (!rtsp_parse_message(pdata, datalen, &dataoff, + &hdrsoff, &hdrslen, + &cseqoff, &cseqlen)) + { + break; /* not a valid message */ + } + + if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0) + { + continue; /* not a SETUP message */ + } + DEBUGP("found a setup message\n"); + + memset(&exp, 0, sizeof(exp)); + + off = 0; + while (nf_mime_nextline(pdata+hdrsoff, hdrslen, &off, + &lineoff, &linelen)) + { + if (linelen == 0) + { + break; + } + if (off > hdrsoff+hdrslen) + { + INFOP("!! overrun !!"); + break; + } + + if (nf_strncasecmp(pdata+hdrsoff+lineoff, "Transport:", 10) == 0) + { + rtsp_parse_transport(pdata+hdrsoff+lineoff, linelen, + &exp.help.exp_rtsp_info); + } + } + + if (exp.help.exp_rtsp_info.loport == 0) + { + DEBUGP("no udp transports found\n"); + continue; /* no udp transports found */ + } + + DEBUGP("udp transport found, ports=(%d,%hu,%hu)\n", + (int)exp.help.exp_rtsp_info.pbtype, + exp.help.exp_rtsp_info.loport, + exp.help.exp_rtsp_info.hiport); + + LOCK_BH(&ip_rtsp_lock); + exp.seq = ntohl(tcph->seq) + hdrsoff; /* mark all the headers */ + exp.help.exp_rtsp_info.len = hdrslen; + + exp.tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; + exp.mask.src.ip = 0xffffffff; + exp.tuple.dst.ip = ct->tuplehash[dir].tuple.src.ip; + exp.mask.dst.ip = 0xffffffff; + exp.tuple.dst.u.udp.port = exp.help.exp_rtsp_info.loport; + exp.mask.dst.u.udp.port = (exp.help.exp_rtsp_info.pbtype == pb_range) ? 0xfffe : 0xffff; + exp.tuple.dst.protonum = IPPROTO_UDP; + exp.mask.dst.protonum = 0xffff; + + DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", + NIPQUAD(exp.tuple.src.ip), + ntohs(exp.tuple.src.u.tcp.port), + NIPQUAD(exp.tuple.dst.ip), + ntohs(exp.tuple.dst.u.tcp.port)); + + /* pass the request off to the nat helper */ + rc = ip_conntrack_expect_related(ct, &exp); + UNLOCK_BH(&ip_rtsp_lock); + if (rc == 0) + { + DEBUGP("ip_conntrack_expect_related succeeded\n"); + } + else + { + INFOP("ip_conntrack_expect_related failed (%d)\n", rc); + } + } + + return NF_ACCEPT; +} + +/* inbound packet: server->client */ +static int +help_in(const struct iphdr* iph, size_t pktlen, + struct ip_conntrack* ct, enum ip_conntrack_info ctinfo) +{ + return NF_ACCEPT; +} + +static int +help(const struct iphdr* iph, size_t pktlen, + 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; + u_int32_t tcplen = pktlen - iph->ihl * 4; + + /* 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; + } + + switch (CTINFO2DIR(ctinfo)) + { + case IP_CT_DIR_ORIGINAL: + help_out(iph, pktlen, ct, ctinfo); + break; + case IP_CT_DIR_REPLY: + help_in(iph, pktlen, ct, ctinfo); + break; + default: + /* oops */ + } + + return NF_ACCEPT; +} + +static struct ip_conntrack_helper rtsp_helpers[MAX_PORTS]; +static char rtsp_names[MAX_PORTS][10]; + +/* This function is intentionally _NOT_ defined as __exit */ +static void +fini(void) +{ + int i; + for (i = 0; i < num_ports; i++) + { + DEBUGP("unregistering port %d\n", ports[i]); + ip_conntrack_helper_unregister(&rtsp_helpers[i]); + } +} + +static int __init +init(void) +{ + int i, ret; + struct ip_conntrack_helper *hlpr; + char *tmpname; + + printk("ip_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n"); + + if (max_outstanding < 1) + { + printk("ip_conntrack_rtsp: max_outstanding must be a positive integer\n"); + return -EBUSY; + } + if (setup_timeout < 0) + { + printk("ip_conntrack_rtsp: setup_timeout must be a positive integer\n"); + return -EBUSY; + } + + /* If no port given, default to standard rtsp port */ + if (ports[0] == 0) + { + ports[0] = RTSP_PORT; + } + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) + { + hlpr = &rtsp_helpers[i]; + memset(hlpr, 0, sizeof(struct ip_conntrack_helper)); + hlpr->tuple.src.u.tcp.port = htons(ports[i]); + hlpr->tuple.dst.protonum = IPPROTO_TCP; + hlpr->mask.src.u.tcp.port = 0xFFFF; + hlpr->mask.dst.protonum = 0xFFFF; + hlpr->max_expected = max_outstanding; + hlpr->timeout = setup_timeout; + hlpr->flags = IP_CT_HELPER_F_REUSE_EXPECT; + hlpr->me = ip_conntrack_rtsp; + hlpr->help = help; + + tmpname = &rtsp_names[i][0]; + if (ports[i] == RTSP_PORT) + { + sprintf(tmpname, "rtsp"); + } + else + { + sprintf(tmpname, "rtsp-%d", i); + } + hlpr->name = tmpname; + + DEBUGP("port #%d: %d\n", i, ports[i]); + + ret = ip_conntrack_helper_register(hlpr); + + if (ret) + { + printk("ip_conntrack_rtsp: ERROR registering port %d\n", ports[i]); + fini(); + return -EBUSY; + } + num_ports++; + } + return 0; +} + +#ifdef CONFIG_IP_NF_NAT_NEEDED +EXPORT_SYMBOL(ip_rtsp_lock); +#endif + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_standalone.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_standalone.c Thu Feb 5 17:30:54 2004 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_standalone.c Sun Apr 18 17:33:11 2004 @@ -74,7 +74,7 @@ len += sprintf(buffer + len, "use=%u proto=%u ", atomic_read(&expect->use), expect->tuple.dst.protonum); len += print_tuple(buffer + len, &expect->tuple, - __ip_ct_find_proto(expect->tuple.dst.protonum)); + ip_ct_find_proto(expect->tuple.dst.protonum)); len += sprintf(buffer + len, "\n"); return len; } @@ -84,7 +84,7 @@ { unsigned int len; struct ip_conntrack_protocol *proto - = __ip_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL] + = ip_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL] .tuple.dst.protonum); len = sprintf(buffer, "%-8s %u %lu ", @@ -121,8 +121,6 @@ unsigned int newlen; IP_NF_ASSERT(hash->ctrack); - MUST_BE_READ_LOCKED(&ip_conntrack_lock); - /* Only count originals */ if (DIRECTION(hash)) return 0; @@ -146,16 +144,20 @@ off_t upto = 0; struct list_head *e; - READ_LOCK(&ip_conntrack_lock); /* Traverse hash; print originals then reply. */ for (i = 0; i < ip_conntrack_htable_size; i++) { - if (LIST_FIND(&ip_conntrack_hash[i], conntrack_iterate, + read_lock_key(i); + if (LIST_FIND(&ip_conntrack_hash[i].list, conntrack_iterate, struct ip_conntrack_tuple_hash *, - buffer, offset, &upto, &len, length)) + buffer, offset, &upto, &len, length)) { + read_unlock_key(i); goto finished; + } + read_unlock_key(i); } /* Now iterate through expecteds. */ + READ_LOCK(&ip_conntrack_expect_lock); for (e = ip_conntrack_expect_list.next; e != &ip_conntrack_expect_list; e = e->next) { unsigned int last_len; @@ -167,12 +169,13 @@ len += print_expect(buffer + len, expect); if (len > length) { len = last_len; + READ_UNLOCK(&ip_conntrack_expect_lock); goto finished; } } + READ_UNLOCK(&ip_conntrack_expect_lock); finished: - READ_UNLOCK(&ip_conntrack_lock); /* `start' hack - see fs/proc/generic.c line ~165 */ *start = (char *)((unsigned int)upto - offset); @@ -259,6 +262,11 @@ 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_max_retrans; +extern int ip_ct_tcp_log_invalid; +extern int ip_ct_tcp_loose; +extern int ip_ct_tcp_be_liberal; +extern int ip_ct_tcp_max_retrans; /* From ip_conntrack_proto_udp.c */ extern unsigned long ip_ct_udp_timeout; @@ -315,6 +323,21 @@ {NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT, "ip_conntrack_generic_timeout", &ip_ct_generic_timeout, sizeof(unsigned int), 0644, NULL, &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_MAX_RETRANS, "ip_conntrack_tcp_timeout_max_retrans", + &ip_ct_tcp_timeout_max_retrans, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_LOG_INVALID, "ip_conntrack_tcp_log_invalid", + &ip_ct_tcp_log_invalid, sizeof(unsigned int), 0644, NULL, + &proc_dointvec}, + {NET_IPV4_NF_CONNTRACK_TCP_LOOSE, "ip_conntrack_tcp_loose", + &ip_ct_tcp_loose, 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_TCP_MAX_RETRANS, "ip_conntrack_tcp_max_retrans", + &ip_ct_tcp_max_retrans, sizeof(unsigned int), 0644, NULL, + &proc_dointvec}, {0} }; @@ -469,7 +492,6 @@ EXPORT_SYMBOL(ip_ct_selective_cleanup); EXPORT_SYMBOL(ip_ct_refresh); EXPORT_SYMBOL(ip_ct_find_proto); -EXPORT_SYMBOL(__ip_ct_find_proto); EXPORT_SYMBOL(ip_ct_find_helper); EXPORT_SYMBOL(ip_conntrack_expect_related); EXPORT_SYMBOL(ip_conntrack_change_expect); @@ -481,6 +503,7 @@ EXPORT_SYMBOL(ip_conntrack_htable_size); EXPORT_SYMBOL(ip_conntrack_expect_list); EXPORT_SYMBOL(ip_conntrack_lock); +EXPORT_SYMBOL(ip_conntrack_expect_lock); EXPORT_SYMBOL(ip_conntrack_hash); EXPORT_SYMBOL_GPL(ip_conntrack_find_get); EXPORT_SYMBOL_GPL(ip_conntrack_put); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_conntrack_talk.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_talk.c --- linux-2.4.26/net/ipv4/netfilter/ip_conntrack_talk.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_conntrack_talk.c Sun Apr 18 17:33:11 2004 @@ -0,0 +1,360 @@ +/* + * 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, + { .tcp = { htons(talk_port) } }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); + + exp->expectfn = talk_expectfn[talk_port - TALK_PORT]; + + DEBUGP("ip_ct_talk_help_response: callee client %u.%u.%u.%u:%u -> caller daemon %u.%u.%u.%u:%u!\n", + NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.udp.port), + NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.udp.port)); + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, &expect); + UNLOCK_BH(&ip_talk_lock); + } + if (type == LOOK_UP) { + + DEBUGP("ip_ct_talk_help_response: LOOK_UP\n"); + + /* update the talk info */ + LOCK_BH(&ip_talk_lock); + exp_talk_info->port = addr->ta_port; + + /* expect callee client -> caller client connection */ + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { addr->ta_addr, + { addr->ta_port }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; + + DEBUGP("ip_ct_talk_help_response: callee client %u.%u.%u.%u:%u -> caller client %u.%u.%u.%u:%u!\n", + NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), + NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, &expect); + UNLOCK_BH(&ip_talk_lock); + } + + return NF_ACCEPT; +} + +/* FIXME: This should be in userspace. Later. */ +static int talk_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + int talk_port, + u_char mode) +{ + struct udphdr *udph = (void *)iph + iph->ihl * 4; + const char *data = (const char *)udph + sizeof(struct udphdr); + int dir = CTINFO2DIR(ctinfo); + size_t udplen; + + DEBUGP("ip_ct_talk_help: help entered\n"); + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ip_ct_talk_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole UDP header? */ + udplen = len - iph->ihl * 4; + if (udplen < sizeof(struct udphdr)) { + DEBUGP("ip_ct_talk_help: too short for udph, udplen = %u\n", (unsigned)udplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + csum_partial((char *)udph, udplen, 0))) { + DEBUGP("ip_ct_talk_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + udph, udplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + DEBUGP("ip_ct_talk_help: %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(udph->source), NIPQUAD(iph->daddr), ntohs(udph->dest)); + + if (dir == IP_CT_DIR_ORIGINAL) + return NF_ACCEPT; + + if (talk_port == TALK_PORT + && udplen == sizeof(struct udphdr) + sizeof(struct talk_response)) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct talk_response *)data)->type, + ((struct talk_response *)data)->answer, + &(((struct talk_response *)data)->addr)); + else if (talk_port == NTALK_PORT + && ntalk + && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_response) + && ((struct ntalk_response *)data)->vers == NTALK_VERSION) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct ntalk_response *)data)->type, + ((struct ntalk_response *)data)->answer, + &(((struct ntalk_response *)data)->addr)); + else if (talk_port == NTALK_PORT + && ntalk2 + && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_response) + && ((struct ntalk2_response *)data)->vers == NTALK2_VERSION) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct ntalk2_response *)data)->type, + ((struct ntalk2_response *)data)->answer, + &(((struct ntalk2_response *)data)->addr)); + else { + DEBUGP("ip_ct_talk_help: not ntalk/ntalk2 response, datalen %u != %u or %u + max 256\n", + (unsigned)udplen - sizeof(struct udphdr), + sizeof(struct ntalk_response), sizeof(struct ntalk2_response)); + return NF_ACCEPT; + } +} + +static int lookup_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, TALK_PORT, LOOK_UP); +} + +static int lookup_nhelp(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, NTALK_PORT, LOOK_UP); +} + +static struct ip_conntrack_helper lookup_helpers[2] = + { { { NULL, NULL }, + "talk", /* name */ + 0, /* flags */ + NULL, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(TALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + lookup_help }, /* helper */ + { { NULL, NULL }, + "ntalk", /* name */ + 0, /* flags */ + NULL, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(NTALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + lookup_nhelp } /* helper */ + }; + +static int talk_expect(struct ip_conntrack *ct) +{ + DEBUGP("ip_conntrack_talk: calling talk_expectfn for ct %p\n", ct); + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &lookup_helpers[0]; + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +static int ntalk_expect(struct ip_conntrack *ct) +{ + DEBUGP("ip_conntrack_talk: calling ntalk_expectfn for ct %p\n", ct); + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &lookup_helpers[1]; + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, TALK_PORT, ANNOUNCE); +} + +static int nhelp(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, NTALK_PORT, ANNOUNCE); +} + +static struct ip_conntrack_helper talk_helpers[2] = + { { { NULL, NULL }, + "talk", /* name */ + 0, /* flags */ + THIS_MODULE, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(TALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + help }, /* helper */ + { { NULL, NULL }, + "ntalk", /* name */ + 0, /* flags */ + THIS_MODULE, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(NTALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + nhelp } /* helper */ + }; + +static int __init init(void) +{ + if (talk > 0) + ip_conntrack_helper_register(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_conntrack_helper_register(&talk_helpers[1]); + + return 0; +} + +static void __exit fini(void) +{ + if (talk > 0) + ip_conntrack_helper_unregister(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_conntrack_helper_unregister(&talk_helpers[1]); +} + +EXPORT_SYMBOL(ip_talk_lock); + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_fw_compat_masq.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_fw_compat_masq.c --- linux-2.4.26/net/ipv4/netfilter/ip_fw_compat_masq.c Mon Jan 5 16:36:40 2004 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_fw_compat_masq.c Sun Apr 18 17:33:05 2004 @@ -282,15 +282,17 @@ offset = 1; } - READ_LOCK(&ip_conntrack_lock); /* Traverse hash; print originals then reply. */ for (i = 0; i < ip_conntrack_htable_size; i++) { - if (LIST_FIND(&ip_conntrack_hash[i], masq_iterate, + read_lock_key(i); + if (LIST_FIND(&ip_conntrack_hash[i].list, masq_iterate, struct ip_conntrack_tuple_hash *, - buffer, offset, &upto, &len, length)) + buffer, offset, &upto, &len, length)) { + read_unlock_key(i); break; + } + read_unlock_key(i); } - READ_UNLOCK(&ip_conntrack_lock); /* `start' hack - see fs/proc/generic.c line ~165 */ *start = (char *)((unsigned int)upto - offset); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_nat_core.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_core.c --- linux-2.4.26/net/ipv4/netfilter/ip_nat_core.c Fri Jan 16 11:26:27 2004 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_core.c Sun Apr 18 17:33:05 2004 @@ -35,7 +35,7 @@ #endif DECLARE_RWLOCK(ip_nat_lock); -DECLARE_RWLOCK_EXTERN(ip_conntrack_lock); +DECLARE_RWLOCK_EXTERN(ip_conntrack_expect_lock); /* Calculated at init based on memory size */ static unsigned int ip_nat_htable_size; @@ -743,9 +743,8 @@ { struct ip_conntrack_protocol *proto; int ret = 1; - - MUST_BE_READ_LOCKED(&ip_conntrack_lock); - proto = __ip_ct_find_proto((*pskb)->nh.iph->protocol); + + proto = ip_ct_find_proto((*pskb)->nh.iph->protocol); if (proto->exp_matches_pkt) ret = proto->exp_matches_pkt(exp, pskb); @@ -814,8 +813,8 @@ & htons(IP_MF|IP_OFFSET))); /* Have to grab read lock before sibling_list traversal */ - READ_LOCK(&ip_conntrack_lock); - list_for_each_prev(cur_item, &ct->sibling_list) { + READ_LOCK(&ip_conntrack_expect_lock); + list_for_each(cur_item, &ct->sibling_list) { exp = list_entry(cur_item, struct ip_conntrack_expect, expected_list); @@ -830,7 +829,7 @@ ret = helper->help(ct, exp, info, ctinfo, hooknum, pskb); if (ret != NF_ACCEPT) { - READ_UNLOCK(&ip_conntrack_lock); + READ_UNLOCK(&ip_conntrack_expect_lock); return ret; } helper_called = 1; @@ -843,11 +842,11 @@ ret = helper->help(ct, NULL, info, ctinfo, hooknum, pskb); if (ret != NF_ACCEPT) { - READ_UNLOCK(&ip_conntrack_lock); + READ_UNLOCK(&ip_conntrack_expect_lock); return ret; } } - READ_UNLOCK(&ip_conntrack_lock); + READ_UNLOCK(&ip_conntrack_expect_lock); /* Adjust sequence number only once per packet * (helper is called at all hooks) */ diff -urN linux-2.4.26/net/ipv4/netfilter/ip_nat_cuseeme.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_cuseeme.c --- linux-2.4.26/net/ipv4/netfilter/ip_nat_cuseeme.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_cuseeme.c Sun Apr 18 17:33:06 2004 @@ -0,0 +1,289 @@ +/* CuSeeMe extension for UDP NAT alteration. + * (C) 2002 by Filip Sneppe + * based on ip_masq_cuseeme.c in 2.2 kernels + * + * ip_nat_cuseeme.c v0.0.7 2003-02-18 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Module load syntax: + * insmod ip_nat_cuseeme.o ports=port1,port2,...port + * + * Please give the ports of the CuSeeMe traffic you want to track. + * If you don't specify ports, the default will be UDP port 7648. + * + * CuSeeMe Protocol Documentation: + * http://cu-seeme.net/squeek/tech/contents.html + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Filip Sneppe "); +MODULE_DESCRIPTION("Netfilter NAT helper for CuSeeMe"); +MODULE_LICENSE("GPL"); + +#define MAX_PORTS 8 + +static int ports[MAX_PORTS]; +static int ports_c = 0; +#ifdef MODULE_PARM +MODULE_PARM(ports,"1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of CuSeeMe reflectors"); +#endif + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/* process packet from client->reflector, possibly manipulate client IP in payload */ +void cuseeme_mangle_outgoing(struct ip_conntrack *ct, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + struct sk_buff **pskb, + char *data, + unsigned int datalen) +{ + char new_port_ip[6]; + struct cu_header *cu_head=(struct cu_header *)data; + + DEBUGP("ip_nat_cuseeme: outgoing packet, ID %u, dest_family %u\n", + ntohs(cu_head->data_type), ntohs(cu_head->dest_family)); + + /* At least check that the data at offset 10 is the client's port and IP address */ + if ((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip == cu_head->addr) && + (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port == cu_head->port)) { + DEBUGP("ip_nat_cuseeme: rewrite outgoing client %u.%u.%u.%u:%u->%u.%u.%u.%u:%u at offset 10\n", + NIPQUAD(cu_head->addr), + ntohs(cu_head->port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port)); + *((u_int16_t *)new_port_ip) = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port; + *((u_int32_t *)(new_port_ip+2)) = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + /* at offset 10, replace 6 bytes containing port + IP address */ + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + 10, 6, (char *)(new_port_ip), 6); + } else + DEBUGP("ip_nat_cuseeme: expected outgoing client %u.%u.%u.%u:%u, but got %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port), + NIPQUAD(cu_head->addr), + ntohs(cu_head->port)); +} + +/* process packet from reflector->client, possibly manipulate client IP & reflector IP in payload */ +void cuseeme_mangle_incoming(struct ip_conntrack *ct, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + struct sk_buff **pskb, + char *data, + unsigned int datalen) +{ + char new_port_ip[6]; + struct cu_header *cu_head = (struct cu_header *)data; + struct oc_header *oc_head = (struct oc_header *)data; + struct client_info *ci; + int i, off; + + + DEBUGP("ip_nat_cuseeme: incoming packet, ID %u, dest_family %u\n", + ntohs(cu_head->data_type), ntohs(cu_head->dest_family)); + + /* Check if we're really dealing with the client's port + IP address before rewriting */ + if((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip == cu_head->dest_addr) && + (ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port == cu_head->dest_port)) { + DEBUGP("ip_nat_cuseeme: rewrite incoming client %u.%u.%u.%u:%u->%u.%u.%u.%u:%u at offset 2\n", + NIPQUAD(cu_head->dest_addr), + ntohs(cu_head->dest_port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port)); + *((u_int16_t *)new_port_ip) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; + *((u_int32_t *)(new_port_ip+2)) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + /* at offset 2, replace 6 bytes containing port + IP address */ + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + 2, 6, (char *)(new_port_ip), 6); + } else + DEBUGP("ip_nat_cuseeme: expected incoming client %u.%u.%u.%u:%u, but got %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port), + NIPQUAD(cu_head->dest_addr), + ntohs(cu_head->dest_port)); + + /* Check if we're really dealing with the server's port + IP address before rewriting. + In some cases, the IP address == 0.0.0.0 so we don't rewrite anything */ + if((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip == cu_head->addr) && + (ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port == cu_head->port)) { + DEBUGP("in_nat_cuseeme: rewrite incoming server %u.%u.%u.%u:%u->%u.%u.%u.%u:%u at offset 10\n", + NIPQUAD(cu_head->addr), + ntohs(cu_head->port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip), + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port)); + *((u_int16_t *)new_port_ip) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port; + *((u_int32_t *)(new_port_ip+2)) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + /* at offset 10, replace 6 bytes containing port + IP address */ + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + 10, 6, (char *)(new_port_ip), 6); + } else + /* Sometimes we find 0.0.0.0, sometimes an IP address - the docs say this field + is not that important so we're not logging this unless we're debugging */ + DEBUGP("ip_nat_cuseeme: no biggie, expected incoming server %u.%u.%u.%u:%u, but got %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip), + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port), + NIPQUAD(cu_head->addr), + ntohs(cu_head->port)); + + /* Spin through client_info structs until we find our own */ + if((ntohs(cu_head->data_type) == 101) && (datalen >= sizeof(struct oc_header))) { + DEBUGP("ip_nat_cuseeme: looping through %u client_info structs\n", oc_head->client_count); + for(i=0, off=sizeof(struct oc_header); + (i < oc_head->client_count && + off+sizeof(struct client_info) <= datalen); + i++) { + ci=(struct client_info *)(data+off); + DEBUGP("ip_nat_cuseeme: comparing %u.%u.%u.%u with %u.%u.%u.%u at offset %u\n", + NIPQUAD(ci->address), NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), + (unsigned int)((void *)&(ci->address) - (void *)cu_head)); + if(ci->address == ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip) { + /* mangle this IP address */ + DEBUGP("ip_nat_cuseeme: changing %u.%u.%u.%u->%u.%u.%u.%u at offset %u\n", + NIPQUAD(ci->address), + NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), + (unsigned int)((void *)&(ci->address) - (void *)cu_head)); + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + (unsigned int)((void *)&(ci->address) - (void *)cu_head), 4, + (char *)(&(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip)), 4); + break; + } else + off+=sizeof(struct client_info); + } + } else + DEBUGP("ip_nat_cuseeme: data_type %u, datalen %u < sizeof(struct oc_header) %u\n", + ntohs(cu_head->data_type), datalen, sizeof(struct oc_header)); +} + +static unsigned int +cuseeme_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + int dir = CTINFO2DIR(ctinfo); + unsigned int datalen = (*pskb)->len - iph->ihl * 4 - sizeof(struct udphdr); + char *data = (char *) &udph[1]; + + DEBUGP("ip_nat_cuseeme: cuseeme_nat_help, direction: %s hook: %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???" + ); + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("ip_nat_cuseeme: not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "????"); + return NF_ACCEPT; + } + + if(datalen < sizeof(struct cu_header)) { + /* packet too small */ + if (net_ratelimit()) + printk("ip_nat_cuseeme: payload too small (%u, should be >= %u)\n", + datalen, sizeof(struct cu_header)); + return NF_ACCEPT; + } + + + /* In the debugging output, "outgoing" is from client to server, and + "incoming" is from server to client */ + if(HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) + cuseeme_mangle_outgoing(ct, info, ctinfo, pskb, data, datalen); + else + cuseeme_mangle_incoming(ct, info, ctinfo, pskb, data, datalen); + + return NF_ACCEPT; +} + +static struct ip_nat_helper cuseeme[MAX_PORTS]; +static char cuseeme_names[MAX_PORTS][14]; /* cuseeme-65535 */ + +static void fini(void) +{ + int i; + + for (i = 0 ; i < ports_c; i++) { + DEBUGP("ip_nat_cuseeme: unregistering helper for port %d\n", ports[i]); + ip_nat_helper_unregister(&cuseeme[i]); + } +} + +static int __init init(void) +{ + int i, ret = 0; + char *tmpname; + + if (!ports[0]) + ports[0] = CUSEEME_PORT; + + for (i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) { + memset(&cuseeme[i], 0, sizeof(struct ip_nat_helper)); + + cuseeme[i].tuple.dst.protonum = IPPROTO_UDP; + cuseeme[i].tuple.dst.u.udp.port = htons(ports[i]); + cuseeme[i].mask.dst.protonum = 0xFFFF; + cuseeme[i].mask.dst.u.udp.port = 0xFFFF; + cuseeme[i].help = cuseeme_nat_help; + cuseeme[i].flags = IP_NAT_HELPER_F_STANDALONE + + IP_NAT_HELPER_F_ALWAYS; /* dunno if IP_NAT_HELPER_F_ALWAYS + is stricly needed... */ + cuseeme[i].me = THIS_MODULE; + cuseeme[i].expect = NULL; /* cuseeme_nat_expected; */ + + tmpname = &cuseeme_names[i][0]; + if (ports[i] == CUSEEME_PORT) + sprintf(tmpname, "cuseeme"); + else + sprintf(tmpname, "cuseeme-%d", ports[i]); + cuseeme[i].name = tmpname; + + DEBUGP("ip_nat_cuseeme: registering helper for port %d: name %s\n", + ports[i], cuseeme[i].name); + ret = ip_nat_helper_register(&cuseeme[i]); + + if (ret) { + printk("ip_nat_cuseeme: unable to register helper for port %d\n", + ports[i]); + fini(); + return ret; + } + ports_c++; + } + return ret; +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_nat_h323.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_h323.c --- linux-2.4.26/net/ipv4/netfilter/ip_nat_h323.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_h323.c Sun Apr 18 17:33:06 2004 @@ -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) + { .tcp = { port } }); + } + + ret = ip_nat_setup_info(ct, &mr, hooknum); + + if (is_h225) { + DEBUGP("h225_nat_expected: H.225, setting NAT helper for %p\n", ct); + /* NAT expectfn called with ip_nat_lock write-locked */ + info->helper = &h245; + } + return ret; +} + +static int h323_signal_address_fixup(struct ip_conntrack *ct, + struct sk_buff **pskb, + enum ip_conntrack_info ctinfo) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl*4; + char *data = (char *) tcph + tcph->doff * 4; + u_int32_t tcplen = (*pskb)->len - iph->ihl*4; + u_int32_t datalen = tcplen - tcph->doff*4; + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + u_int32_t newip; + u_int16_t port; + int i; + + MUST_BE_LOCKED(&ip_h323_lock); + + DEBUGP("h323_signal_address_fixup: %s %s\n", + between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + ? "yes" : "no", + between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + ? "yes" : "no"); + if (!(between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + || between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen))) + return 1; + + DEBUGP("h323_signal_address_fixup: offsets %u + 6 and %u + 6 in %u\n", + info->offset[IP_CT_DIR_ORIGINAL], + info->offset[IP_CT_DIR_REPLY], + tcplen); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + for (i = 0; i < IP_CT_DIR_MAX; i++) { + DEBUGP("h323_signal_address_fixup: %s %s\n", + info->dir == IP_CT_DIR_ORIGINAL ? "original" : "reply", + i == IP_CT_DIR_ORIGINAL ? "caller" : "callee"); + if (!between(info->seq[i], ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) + continue; + if (!between(info->seq[i] + 6, ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + /* Partial retransmisison. It's a cracker being funky. */ + if (net_ratelimit()) { + printk("H.323_NAT: partial packet %u/6 in %u/%u\n", + info->seq[i], + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + return 0; + } + + /* Change address inside packet to match way we're mapping + this connection. */ + if (i == IP_CT_DIR_ORIGINAL) { + newip = ct->tuplehash[!info->dir].tuple.dst.ip; + port = ct->tuplehash[!info->dir].tuple.dst.u.tcp.port; + } else { + newip = ct->tuplehash[!info->dir].tuple.src.ip; + port = ct->tuplehash[!info->dir].tuple.src.u.tcp.port; + } + + DEBUGP("h323_signal_address_fixup: orig %s IP:port %u.%u.%u.%u:%u\n", + i == IP_CT_DIR_ORIGINAL ? "source" : "dest ", + NIPQUAD(*((u_int32_t *)(data + info->offset[i]))), + ntohs(*((u_int16_t *)(data + info->offset[i] + 4)))); + + /* Modify the packet */ + *(u_int32_t *)(data + info->offset[i]) = newip; + *(u_int16_t *)(data + info->offset[i] + 4) = port; + + DEBUGP("h323_signal_address_fixup: new %s IP:port %u.%u.%u.%u:%u\n", + i == IP_CT_DIR_ORIGINAL ? "source" : "dest ", + NIPQUAD(*((u_int32_t *)(data + info->offset[i]))), + ntohs(*((u_int16_t *)(data + info->offset[i] + 4)))); + } + + /* fix checksum information */ + + (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4, + datalen, 0); + + tcph->check = 0; + tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcph->doff*4, + (*pskb)->csum)); + ip_send_check(iph); + + return 1; +} + +static int h323_data_fixup(struct ip_ct_h225_expect *info, + struct ip_conntrack *ct, + struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) +{ + u_int32_t newip; + u_int16_t port; + struct ip_conntrack_tuple newtuple; + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl*4; + char *data = (char *) tcph + tcph->doff * 4; + u_int32_t tcplen = (*pskb)->len - iph->ihl*4; + struct ip_ct_h225_master *master_info = &ct->help.ct_h225_info; + int is_h225; + + MUST_BE_LOCKED(&ip_h323_lock); + DEBUGP("h323_data_fixup: offset %u + 6 in %u\n", info->offset, tcplen); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + if (!between(expect->seq + 6, ntohl(tcph->seq), + ntohl(tcph->seq) + tcplen - tcph->doff * 4)) { + /* Partial retransmisison. It's a cracker being funky. */ + if (net_ratelimit()) { + printk("H.323_NAT: partial packet %u/6 in %u/%u\n", + expect->seq, + ntohl(tcph->seq), + ntohl(tcph->seq) + tcplen - tcph->doff * 4); + } + return 0; + } + + /* Change address inside packet to match way we're mapping + this connection. */ + if (info->dir == IP_CT_DIR_REPLY) { + /* Must be where client thinks server is */ + newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + /* Expect something from client->server */ + newtuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + } else { + /* Must be where server thinks client is */ + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + /* Expect something from server->client */ + newtuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + } + + is_h225 = (master_info->is_h225 == H225_PORT); + + if (is_h225) { + newtuple.dst.protonum = IPPROTO_TCP; + newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port; + } else { + newtuple.dst.protonum = IPPROTO_UDP; + newtuple.src.u.udp.port = expect->tuple.src.u.udp.port; + } + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(info->port); port != 0; port++) { + if (is_h225) + newtuple.dst.u.tcp.port = htons(port); + else + newtuple.dst.u.udp.port = htons(port); + + if (ip_conntrack_change_expect(expect, &newtuple) == 0) + break; + } + if (port == 0) { + DEBUGP("h323_data_fixup: no free port found!\n"); + return 0; + } + + port = htons(port); + + DEBUGP("h323_data_fixup: orig IP:port %u.%u.%u.%u:%u\n", + NIPQUAD(*((u_int32_t *)(data + info->offset))), + ntohs(*((u_int16_t *)(data + info->offset + 4)))); + + /* Modify the packet */ + *(u_int32_t *)(data + info->offset) = newip; + *(u_int16_t *)(data + info->offset + 4) = port; + + DEBUGP("h323_data_fixup: new IP:port %u.%u.%u.%u:%u\n", + NIPQUAD(*((u_int32_t *)(data + info->offset))), + ntohs(*((u_int16_t *)(data + info->offset + 4)))); + + /* fix checksum information */ + /* FIXME: usually repeated multiple times in the case of H.245! */ + + (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4, + tcplen - tcph->doff*4, 0); + + tcph->check = 0; + tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcph->doff*4, + (*pskb)->csum)); + ip_send_check(iph); + + return 1; +} + +static unsigned int h225_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + int dir; + struct ip_ct_h225_expect *exp_info; + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + dir = CTINFO2DIR(ctinfo); + DEBUGP("nat_h323: dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("nat_h323: Not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + return NF_ACCEPT; + } + + if (!exp) { + LOCK_BH(&ip_h323_lock); + if (!h323_signal_address_fixup(ct, pskb, ctinfo)) { + UNLOCK_BH(&ip_h323_lock); + return NF_DROP; + } + UNLOCK_BH(&ip_h323_lock); + return NF_ACCEPT; + } + + exp_info = &exp->help.exp_h225_info; + + LOCK_BH(&ip_h323_lock); + if (!h323_data_fixup(exp_info, ct, pskb, ctinfo, exp)) { + UNLOCK_BH(&ip_h323_lock); + return NF_DROP; + } + UNLOCK_BH(&ip_h323_lock); + + return NF_ACCEPT; +} + +static struct ip_nat_helper h225 = + { { NULL, NULL }, + "H.225", /* name */ + IP_NAT_HELPER_F_ALWAYS, /* flags */ + THIS_MODULE, /* module */ + { { 0, { .tcp = { __constant_htons(H225_PORT) } } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { .tcp = { 0xFFFF } } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h225_nat_help, /* helper */ + h225_nat_expected /* expectfn */ + }; + +static int __init init(void) +{ + int ret; + + ret = ip_nat_helper_register(&h225); + + if (ret != 0) + printk("ip_nat_h323: cannot initialize the module!\n"); + + return ret; +} + +static void __exit fini(void) +{ + ip_nat_helper_unregister(&h225); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_nat_helper.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_helper.c --- linux-2.4.26/net/ipv4/netfilter/ip_nat_helper.c Thu Oct 23 10:11:37 2003 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_helper.c Sun Apr 18 17:33:11 2004 @@ -452,6 +452,8 @@ ip_nat_sack_adjust(skb, ct, ctinfo); + ip_conntrack_tcp_update(skb, ct, dir); + return 0; } diff -urN linux-2.4.26/net/ipv4/netfilter/ip_nat_mms.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_mms.c --- linux-2.4.26/net/ipv4/netfilter/ip_nat_mms.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_mms.c Sun Apr 18 17:33:07 2004 @@ -0,0 +1,350 @@ +/* MMS extension for TCP NAT alteration. + * (C) 2002 by Filip Sneppe + * based on ip_nat_ftp.c and ip_nat_irc.c + * + * ip_nat_mms.c v0.3 2002-09-22 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Module load syntax: + * insmod ip_nat_mms.o ports=port1,port2,...port + * + * Please give the ports of all MMS servers You wish to connect to. + * If you don't specify ports, the default will be TCP port 1755. + * + * More info on MMS protocol, firewalls and NAT: + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/MMSFirewall.asp + * http://www.microsoft.com/windows/windowsmedia/serve/firewall.asp + * + * The SDP project people are reverse-engineering MMS: + * http://get.to/sdp + */ + +/* FIXME: issue with UDP & fragmentation with this URL: + http://www.cnn.com/video/world/2002/01/21/jb.shoe.bomb.cafe.cnn.low.asx + may be related to out-of-order first packets: + basically the expectation is set up correctly, then the server sends + a first UDP packet which is fragmented plus arrives out-of-order. + the MASQUERADING firewall with ip_nat_mms loaded responds with + an ICMP unreachable back to the server */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#define DUMP_BYTES(address, counter) \ +({ \ + int temp_counter; \ + for(temp_counter=0; temp_counter"); +MODULE_DESCRIPTION("Microsoft Windows Media Services (MMS) NAT module"); +MODULE_LICENSE("GPL"); + +DECLARE_LOCK_EXTERN(ip_mms_lock); + +/* FIXME: Time out? --RR */ + +static int mms_data_fixup(const struct ip_ct_mms_expect *ct_mms_info, + struct ip_conntrack *ct, + struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) +{ + u_int32_t newip; + struct ip_conntrack_tuple t; + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *) iph + iph->ihl * 4; + char *data = (char *)tcph + tcph->doff * 4; + int i, j, k, port; + u_int16_t mms_proto; + + u_int32_t *mms_chunkLenLV = (u_int32_t *)(data + MMS_SRV_CHUNKLENLV_OFFSET); + u_int32_t *mms_chunkLenLM = (u_int32_t *)(data + MMS_SRV_CHUNKLENLM_OFFSET); + u_int32_t *mms_messageLength = (u_int32_t *)(data + MMS_SRV_MESSAGELENGTH_OFFSET); + + int zero_padding; + + char buffer[28]; /* "\\255.255.255.255\UDP\65635" * 2 (for unicode) */ + char unicode_buffer[75]; /* 27*2 (unicode) + 20 + 1 */ + char proto_string[6]; + + MUST_BE_LOCKED(&ip_mms_lock); + + /* what was the protocol again ? */ + mms_proto = expect->tuple.dst.protonum; + sprintf(proto_string, "%u", mms_proto); + + DEBUGP("ip_nat_mms: mms_data_fixup: info (seq %u + %u) in %u, proto %s\n", + expect->seq, ct_mms_info->len, ntohl(tcph->seq), + mms_proto == IPPROTO_UDP ? "UDP" + : mms_proto == IPPROTO_TCP ? "TCP":proto_string); + + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + + /* Alter conntrack's expectations. */ + t = expect->tuple; + t.dst.ip = newip; + for (port = ct_mms_info->port; port != 0; port++) { + t.dst.u.tcp.port = htons(port); + if (ip_conntrack_change_expect(expect, &t) == 0) { + DEBUGP("ip_nat_mms: mms_data_fixup: using port %d\n", port); + break; + } + } + + if(port == 0) + return 0; + + sprintf(buffer, "\\\\%u.%u.%u.%u\\%s\\%u", + NIPQUAD(newip), + expect->tuple.dst.protonum == IPPROTO_UDP ? "UDP" + : expect->tuple.dst.protonum == IPPROTO_TCP ? "TCP":proto_string, + port); + DEBUGP("ip_nat_mms: new unicode string=%s\n", buffer); + + memset(unicode_buffer, 0, sizeof(char)*75); + + for (i=0; ipadding, ct_mms_info->len); + DEBUGP("ip_nat_mms: mms_data_fixup: offset: %u\n", MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len); + DUMP_BYTES(data+MMS_SRV_UNICODE_STRING_OFFSET, 60); + + /* add end of packet to it */ + for (j=0; jpadding; ++j) { + DEBUGP("ip_nat_mms: mms_data_fixup: i=%u j=%u byte=%u\n", + i, j, (u8)*(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j)); + *(unicode_buffer+i*2+j) = *(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j); + } + + /* pad with zeroes at the end ? see explanation of weird math below */ + zero_padding = (8-(strlen(buffer)*2 + ct_mms_info->padding + 4)%8)%8; + for (k=0; k chunkLenLV=%u chunkLenLM=%u messageLength=%u\n", + *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength); + + /* explanation, before I forget what I did: + strlen(buffer)*2 + ct_mms_info->padding + 4 must be divisable by 8; + divide by 8 and add 3 to compute the mms_chunkLenLM field, + but note that things may have to be padded with zeroes to align by 8 + bytes, hence we add 7 and divide by 8 to get the correct length */ + *mms_chunkLenLM = (u_int32_t) (3+(strlen(buffer)*2+ct_mms_info->padding+11)/8); + *mms_chunkLenLV = *mms_chunkLenLM+2; + *mms_messageLength = *mms_chunkLenLV*8; + + DEBUGP("ip_nat_mms: modified=> chunkLenLV=%u chunkLenLM=%u messageLength=%u\n", + *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength); + + ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + expect->seq - ntohl(tcph->seq), + ct_mms_info->len + ct_mms_info->padding, unicode_buffer, + strlen(buffer)*2 + ct_mms_info->padding + zero_padding); + DUMP_BYTES(unicode_buffer, 60); + + return 1; +} + +static unsigned int +mms_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + struct ip_nat_multi_range mr; + u_int32_t newdstip, newsrcip, newip; + + struct ip_conntrack *master = master_ct(ct); + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + + IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); + + DEBUGP("ip_nat_mms: mms_nat_expected: We have a connection!\n"); + + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + DEBUGP("ip_nat_mms: mms_nat_expected: hook %s: newsrc->newdst %u.%u.%u.%u->%u.%u.%u.%u\n", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) + newip = newsrcip; + else + newip = newdstip; + + DEBUGP("ip_nat_mms: mms_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip)); + + mr.rangesize = 1; + /* We don't want to manip the per-protocol, just the IPs. */ + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + return ip_nat_setup_info(ct, &mr, hooknum); +} + + +static unsigned int mms_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *) iph + iph->ihl * 4; + unsigned int datalen; + int dir; + struct ip_ct_mms_expect *ct_mms_info; + + if (!exp) + DEBUGP("ip_nat_mms: no exp!!"); + + ct_mms_info = &exp->help.exp_mms_info; + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + dir = CTINFO2DIR(ctinfo); + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + ||(hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("ip_nat_mms: mms_nat_help: not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + return NF_ACCEPT; + } + DEBUGP("ip_nat_mms: mms_nat_help: beyond not touching (dir %s at hook %s)\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + + datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; + + DEBUGP("ip_nat_mms: mms_nat_help: %u+%u=%u %u %u\n", exp->seq, ct_mms_info->len, + exp->seq + ct_mms_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + + LOCK_BH(&ip_mms_lock); + /* Check wether the whole IP/proto/port pattern is carried in the payload */ + if (between(exp->seq + ct_mms_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + if (!mms_data_fixup(ct_mms_info, ct, pskb, ctinfo, exp)) { + UNLOCK_BH(&ip_mms_lock); + return NF_DROP; + } + } else { + /* Half a match? This means a partial retransmisison. + It's a cracker being funky. */ + if (net_ratelimit()) { + printk("ip_nat_mms: partial packet %u/%u in %u/%u\n", + exp->seq, ct_mms_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + UNLOCK_BH(&ip_mms_lock); + return NF_DROP; + } + UNLOCK_BH(&ip_mms_lock); + + return NF_ACCEPT; +} + +static struct ip_nat_helper mms[MAX_PORTS]; +static char mms_names[MAX_PORTS][10]; + +/* Not __exit: called from init() */ +static void fini(void) +{ + int i; + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + DEBUGP("ip_nat_mms: unregistering helper for port %d\n", ports[i]); + ip_nat_helper_unregister(&mms[i]); + } +} + +static int __init init(void) +{ + int i, ret = 0; + char *tmpname; + + if (ports[0] == 0) + ports[0] = MMS_PORT; + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + + memset(&mms[i], 0, sizeof(struct ip_nat_helper)); + + mms[i].tuple.dst.protonum = IPPROTO_TCP; + mms[i].tuple.src.u.tcp.port = htons(ports[i]); + mms[i].mask.dst.protonum = 0xFFFF; + mms[i].mask.src.u.tcp.port = 0xFFFF; + mms[i].help = mms_nat_help; + mms[i].me = THIS_MODULE; + mms[i].flags = 0; + mms[i].expect = mms_nat_expected; + + tmpname = &mms_names[i][0]; + if (ports[i] == MMS_PORT) + sprintf(tmpname, "mms"); + else + sprintf(tmpname, "mms-%d", i); + mms[i].name = tmpname; + + DEBUGP("ip_nat_mms: register helper for port %d\n", + ports[i]); + ret = ip_nat_helper_register(&mms[i]); + + if (ret) { + printk("ip_nat_mms: error registering " + "helper for port %d\n", ports[i]); + fini(); + return ret; + } + ports_c++; + } + + return ret; +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_nat_quake3.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_quake3.c --- linux-2.4.26/net/ipv4/netfilter/ip_nat_quake3.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_quake3.c Sun Apr 18 17:33:08 2004 @@ -0,0 +1,249 @@ +/* Quake3 extension for UDP NAT alteration. + * (C) 2002 by Filip Sneppe + * based on ip_nat_ftp.c and ip_nat_tftp.c + * + * ip_nat_quake3.c v0.0.3 2002-08-31 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Module load syntax: + * insmod ip_nat_quake3.o ports=port1,port2,...port + * + * please give the ports of all Quake3 master servers You wish to + * connect to. If you don't specify ports, the default will be UDP + * port 27950. + * + * Thanks to the Ethereal folks for their analysis of the Quake3 protocol. + * + * Notes: + * - If you're one of those people who would try anything to lower + * latency while playing Quake (and who isn't :-) ), you may want to + * consider not loading ip_nat_quake3 at all and just MASQUERADE all + * outgoing UDP traffic. + * This will make ip_conntrack_quake3 add the necessary expectations, + * but there will be no overhead for client->server UDP streams. If + * ip_nat_quake3 is loaded, quake3_nat_expected will be called per NAT + * hook for every packet in the client->server UDP stream. + * - Only SNAT/MASQUEARDE targets are useful for ip_nat_quake3. + * The IP addresses in the master connection payload (=IP addresses + * of Quake servers) have no relation with the master server so + * DNAT'ing the master connection to a server should not change the + * expected connections. + * - Not tested due to lack of equipment: + * - multiple Quake3 clients behind one MASQUERADE gateway + * - what if Quake3 client is running on router too + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Filip Sneppe "); +MODULE_DESCRIPTION("Netfilter NAT helper for Quake III Arena"); +MODULE_LICENSE("GPL"); + +#define MAX_PORTS 8 + +static int ports[MAX_PORTS]; +static int ports_c = 0; +#ifdef MODULE_PARM +MODULE_PARM(ports,"1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of Quake III master servers"); +#endif + +/* Quake3 master server reply will add > 100 expectations per reply packet; when + doing lots of printk's, klogd may not be able to read /proc/kmsg fast enough */ +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static struct quake3_search quake3s_nat = { "****", "getserversResponse", sizeof("getserversResponse") - 1 }; + +static unsigned int +quake3_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + struct ip_conntrack_tuple repl; + int dir = CTINFO2DIR(ctinfo); + int i; + + DEBUGP("ip_nat_quake3: quake3_nat_help, direction: %s hook: %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???" + ); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("ip_nat_quake3: Not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "????"); + return NF_ACCEPT; + } + + if (!exp) { + DEBUGP("no conntrack expectation to modify\n"); + return NF_ACCEPT; + } + + if (strnicmp((const char *)udph + 12, quake3s_nat.pattern, quake3s_nat.plen) == 0) { + for(i=31; /* 8 bytes UDP hdr, 4 bytes filler, 18 bytes "getserversResponse", 1 byte "\" */ + i+6 < ntohs(udph->len); + i+=7) { + DEBUGP("ip_nat_quake3: adding server at offset %u/%u %u.%u.%u.%u:%u\n", + i, ntohs(udph->len), + NIPQUAD( (u_int32_t) *( (u_int32_t *)( (int)udph + i ) ) ), + ntohs((__u16) *( (__u16 *)( (int)udph + i + 4 ) ) ) ); + + memset(&repl, 0, sizeof(repl)); + + repl.dst.protonum = IPPROTO_UDP; + repl.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + repl.dst.ip = *( (u_int32_t *)( (int)udph + i ) ); + repl.dst.u.udp.port = (__u16) *( (__u16 *)( (int)udph + i + 4 ) ); + + ip_conntrack_change_expect(exp, &repl); + } + } + return NF_ACCEPT; +} + +static unsigned int +quake3_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + const struct ip_conntrack *master = ct->master->expectant; + struct ip_nat_multi_range mr; + u_int32_t newsrcip, newdstip, newip; +#if 0 + const struct ip_conntrack_tuple *repl = + &master->tuplehash[IP_CT_DIR_REPLY].tuple; + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl*4; +#endif + + DEBUGP("ip_nat_quake3: quake3_nat_expected: here we are\n"); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); + + newdstip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) { + newip = newsrcip; + DEBUGP("hook: %s orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u " + "newsrc: %u.%u.%u.%u\n", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "????", + NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source), + NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest), + NIPQUAD(newip)); + + } else { + newip = newdstip; + DEBUGP("hook: %s orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u " + "newdst: %u.%u.%u.%u\n", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "????", + NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source), + NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest), + NIPQUAD(newip)); + } + + mr.rangesize = 1; + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + return ip_nat_setup_info(ct,&mr,hooknum); +} + +static struct ip_nat_helper quake3[MAX_PORTS]; +static char quake3_names[MAX_PORTS][13]; /* quake3-65535 */ + +static void fini(void) +{ + int i; + + for (i = 0 ; i < ports_c; i++) { + DEBUGP("ip_nat_quake3: unregistering helper for port %d\n", ports[i]); + ip_nat_helper_unregister(&quake3[i]); + } +} + +static int __init init(void) + { + int i, ret = 0; + char *tmpname; + + if (!ports[0]) + ports[0] = QUAKE3_MASTER_PORT; + + for (i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) { + memset(&quake3[i], 0, sizeof(struct ip_nat_helper)); + + quake3[i].tuple.dst.protonum = IPPROTO_UDP; + quake3[i].tuple.src.u.udp.port = htons(ports[i]); + quake3[i].mask.dst.protonum = 0xFFFF; + quake3[i].mask.src.u.udp.port = 0xFFFF; + quake3[i].help = quake3_nat_help; + quake3[i].flags = 0; + quake3[i].me = THIS_MODULE; + quake3[i].expect = quake3_nat_expected; + + tmpname = &quake3_names[i][0]; + if (ports[i] == QUAKE3_MASTER_PORT) + sprintf(tmpname, "quake3"); + else + sprintf(tmpname, "quake3-%d", i); + quake3[i].name = tmpname; + + DEBUGP("ip_nat_quake3: registering helper for port %d: name %s\n", + ports[i], quake3[i].name); + ret = ip_nat_helper_register(&quake3[i]); + + if (ret) { + printk("ip_nat_quake3: unable to register helper for port %d\n", + ports[i]); + fini(); + return ret; + } + ports_c++; + } + return ret; + } + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_nat_rtsp.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_rtsp.c --- linux-2.4.26/net/ipv4/netfilter/ip_nat_rtsp.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_rtsp.c Sun Apr 18 17:33:10 2004 @@ -0,0 +1,625 @@ +/* + * RTSP extension for TCP NAT alteration + * (C) 2003 by Tom Marshall + * based on ip_nat_irc.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Module load syntax: + * insmod ip_nat_rtsp.o ports=port1,port2,...port + * stunaddr=
+ * destaction=[auto|strip|none] + * + * If no ports are specified, the default will be port 554 only. + * + * stunaddr specifies the address used to detect that a client is using STUN. + * If this address is seen in the destination parameter, it is assumed that + * the client has already punched a UDP hole in the firewall, so we don't + * mangle the client_port. If none is specified, it is autodetected. It + * only needs to be set if you have multiple levels of NAT. It should be + * set to the external address that the STUN clients detect. Note that in + * this case, it will not be possible for clients to use UDP with servers + * between the NATs. + * + * If no destaction is specified, auto is used. + * destaction=auto: strip destination parameter if it is not stunaddr. + * destaction=strip: always strip destination parameter (not recommended). + * destaction=none: do not touch destination parameter (not recommended). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#define NF_NEED_STRNCASECMP +#define NF_NEED_STRTOU16 +#include +#define NF_NEED_MIME_NEXTLINE +#include + +#define INFOP(args...) printk(KERN_INFO __FILE__ ":" __FUNCTION__ ":" args) +#ifdef IP_NF_RTSP_DEBUG +#define DEBUGP(args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ ":" args); +#else +#define DEBUGP(args...) +#endif + +#define MAX_PORTS 8 +#define DSTACT_AUTO 0 +#define DSTACT_STRIP 1 +#define DSTACT_NONE 2 + +static int ports[MAX_PORTS]; +static char* stunaddr = NULL; +static char* destaction = NULL; + +static int num_ports = 0; +static u_int32_t extip = 0; +static int dstact = 0; + +MODULE_AUTHOR("Tom Marshall "); +MODULE_DESCRIPTION("RTSP network address translation module"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of RTSP servers"); +MODULE_PARM(stunaddr, "s"); +MODULE_PARM_DESC(stunaddr, "Address for detecting STUN"); +MODULE_PARM(destaction, "s"); +MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)"); +#endif + +/* protects rtsp part of conntracks */ +DECLARE_LOCK_EXTERN(ip_rtsp_lock); + +#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; } + +/*** helper functions ***/ + +static void +get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen) +{ + struct iphdr* iph = (struct iphdr*)skb->nh.iph; + struct tcphdr* tcph = (struct tcphdr*)((char*)iph + iph->ihl*4); + + *pptcpdata = (char*)tcph + tcph->doff*4; + *ptcpdatalen = ((char*)skb->h.raw + skb->len) - *pptcpdata; +} + +/*** nat functions ***/ + +/* + * Mangle the "Transport:" header: + * - Replace all occurences of "client_port=" + * - Handle destination parameter + * + * In: + * ct, ctinfo = conntrack context + * pskb = packet + * tranoff = Transport header offset from TCP data + * tranlen = Transport header length (incl. CRLF) + * rport_lo = replacement low port (host endian) + * rport_hi = replacement high port (host endian) + * + * Returns packet size difference. + * + * Assumes that a complete transport header is present, ending with CR or LF + */ +static int +rtsp_mangle_tran(struct ip_conntrack* ct, enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect* exp, + struct sk_buff** pskb, uint tranoff, uint tranlen) +{ + char* ptcp; + uint tcplen; + char* ptran; + char rbuf1[16]; /* Replacement buffer (one port) */ + uint rbuf1len; /* Replacement len (one port) */ + char rbufa[16]; /* Replacement buffer (all ports) */ + uint rbufalen; /* Replacement len (all ports) */ + u_int32_t newip; + u_int16_t loport, hiport; + uint off = 0; + uint diff; /* Number of bytes we removed */ + + struct ip_ct_rtsp_expect* prtspexp = &exp->help.exp_rtsp_info; + struct ip_conntrack_tuple t; + + char szextaddr[15+1]; + uint extaddrlen; + int is_stun; + + get_skb_tcpdata(*pskb, &ptcp, &tcplen); + ptran = ptcp+tranoff; + + if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen || + tranlen < 10 || !iseol(ptran[tranlen-1]) || + nf_strncasecmp(ptran, "Transport:", 10) != 0) + { + INFOP("sanity check failed\n"); + return 0; + } + off += 10; + SKIP_WSPACE(ptcp+tranoff, tranlen, off); + + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + t = exp->tuple; + t.dst.ip = newip; + + extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(extip)) + : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(newip)); + DEBUGP("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto")); + + rbuf1len = rbufalen = 0; + switch (prtspexp->pbtype) + { + case pb_single: + for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ + { + t.dst.u.udp.port = htons(loport); + if (ip_conntrack_change_expect(exp, &t) == 0) + { + DEBUGP("using port %hu\n", loport); + break; + } + } + if (loport != 0) + { + rbuf1len = sprintf(rbuf1, "%hu", loport); + rbufalen = sprintf(rbufa, "%hu", loport); + } + break; + case pb_range: + for (loport = prtspexp->loport; loport != 0; loport += 2) /* XXX: improper wrap? */ + { + t.dst.u.udp.port = htons(loport); + if (ip_conntrack_change_expect(exp, &t) == 0) + { + hiport = loport + ~exp->mask.dst.u.udp.port; + DEBUGP("using ports %hu-%hu\n", loport, hiport); + break; + } + } + if (loport != 0) + { + rbuf1len = sprintf(rbuf1, "%hu", loport); + rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1); + } + break; + case pb_discon: + for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ + { + t.dst.u.udp.port = htons(loport); + if (ip_conntrack_change_expect(exp, &t) == 0) + { + DEBUGP("using port %hu (1 of 2)\n", loport); + break; + } + } + for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */ + { + t.dst.u.udp.port = htons(hiport); + if (ip_conntrack_change_expect(exp, &t) == 0) + { + DEBUGP("using port %hu (2 of 2)\n", hiport); + break; + } + } + if (loport != 0 && hiport != 0) + { + rbuf1len = sprintf(rbuf1, "%hu", loport); + if (hiport == loport+1) + { + rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport); + } + else + { + rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport); + } + } + break; + default: + /* oops */ + } + + if (rbuf1len == 0) + { + return 0; /* cannot get replacement port(s) */ + } + + /* Transport: tran;field;field=val,tran;field;field=val,... */ + while (off < tranlen) + { + uint saveoff; + const char* pparamend; + uint nextparamoff; + + pparamend = memchr(ptran+off, ',', tranlen-off); + pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; + nextparamoff = pparamend-ptcp; + + /* + * We pass over each param twice. On the first pass, we look for a + * destination= field. It is handled by the security policy. If it + * is present, allowed, and equal to our external address, we assume + * that STUN is being used and we leave the client_port= field alone. + */ + is_stun = 0; + saveoff = off; + while (off < nextparamoff) + { + const char* pfieldend; + uint nextfieldoff; + + pfieldend = memchr(ptran+off, ';', nextparamoff-off); + nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; + + if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0) + { + if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0) + { + is_stun = 1; + } + if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun)) + { + diff = nextfieldoff-off; + if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + off, diff, NULL, 0)) + { + /* mangle failed, all we can do is bail */ + return 0; + } + get_skb_tcpdata(*pskb, &ptcp, &tcplen); + ptran = ptcp+tranoff; + tranlen -= diff; + nextparamoff -= diff; + nextfieldoff -= diff; + } + } + + off = nextfieldoff; + } + if (is_stun) + { + continue; + } + off = saveoff; + while (off < nextparamoff) + { + const char* pfieldend; + uint nextfieldoff; + + pfieldend = memchr(ptran+off, ';', nextparamoff-off); + nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; + + if (strncmp(ptran+off, "client_port=", 12) == 0) + { + u_int16_t port; + uint numlen; + uint origoff; + uint origlen; + char* rbuf = rbuf1; + uint rbuflen = rbuf1len; + + off += 12; + origoff = (ptran-ptcp)+off; + origlen = 0; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + origlen += numlen; + if (port != prtspexp->loport) + { + DEBUGP("multiple ports found, port %hu ignored\n", port); + } + else + { + if (ptran[off] == '-' || ptran[off] == '/') + { + off++; + origlen++; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + origlen += numlen; + rbuf = rbufa; + rbuflen = rbufalen; + } + + /* + * note we cannot just memcpy() if the sizes are the same. + * the mangle function does skb resizing, checks for a + * cloned skb, and updates the checksums. + * + * parameter 4 below is offset from start of tcp data. + */ + diff = origlen-rbuflen; + if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + origoff, origlen, rbuf, rbuflen)) + { + /* mangle failed, all we can do is bail */ + return 0; + } + get_skb_tcpdata(*pskb, &ptcp, &tcplen); + ptran = ptcp+tranoff; + tranlen -= diff; + nextparamoff -= diff; + nextfieldoff -= diff; + } + } + + off = nextfieldoff; + } + + off = nextparamoff; + } + + return 1; +} + +static unsigned int +expected(struct sk_buff **pskb, uint hooknum, struct ip_conntrack* ct, struct ip_nat_info* info) +{ + struct ip_nat_multi_range mr; + u_int32_t newdstip, newsrcip, newip; + + struct ip_conntrack *master = master_ct(ct); + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + + IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); + + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip; + + DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.%u.%u\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip), NIPQUAD(newip)); + + mr.rangesize = 1; + /* We don't want to manip the per-protocol, just the IPs. */ + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + return ip_nat_setup_info(ct, &mr, hooknum); +} + +static uint +help_out(struct ip_conntrack* ct, enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect* exp, struct sk_buff** pskb) +{ + char* ptcp; + uint tcplen; + uint hdrsoff; + uint hdrslen; + uint lineoff; + uint linelen; + uint off; + + struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph; + struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4); + + struct ip_ct_rtsp_expect* prtspexp = &exp->help.exp_rtsp_info; + + get_skb_tcpdata(*pskb, &ptcp, &tcplen); + + hdrsoff = exp->seq - ntohl(tcph->seq); + hdrslen = prtspexp->len; + off = hdrsoff; + + while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen)) + { + if (linelen == 0) + { + break; + } + if (off > hdrsoff+hdrslen) + { + INFOP("!! overrun !!"); + break; + } + DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); + + if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) + { + uint oldtcplen = tcplen; + if (!rtsp_mangle_tran(ct, ctinfo, exp, pskb, lineoff, linelen)) + { + break; + } + get_skb_tcpdata(*pskb, &ptcp, &tcplen); + hdrslen -= (oldtcplen-tcplen); + off -= (oldtcplen-tcplen); + lineoff -= (oldtcplen-tcplen); + linelen -= (oldtcplen-tcplen); + DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); + } + } + + return NF_ACCEPT; +} + +static uint +help_in(struct ip_conntrack* ct, enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect* exp, struct sk_buff** pskb) +{ + /* XXX: unmangle */ + return NF_ACCEPT; +} + +static uint +help(struct ip_conntrack* ct, + struct ip_conntrack_expect* exp, + struct ip_nat_info* info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff** pskb) +{ + struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph; + struct tcphdr* tcph = (struct tcphdr*)((char*)iph + iph->ihl * 4); + uint datalen; + int dir; + struct ip_ct_rtsp_expect* ct_rtsp_info; + int rc = NF_ACCEPT; + + if (ct == NULL || exp == NULL || info == NULL || pskb == NULL) + { + DEBUGP("!! null ptr (%p,%p,%p,%p) !!\n", ct, exp, info, pskb); + return NF_ACCEPT; + } + + ct_rtsp_info = &exp->help.exp_rtsp_info; + + /* + * Only mangle things once: original direction in POST_ROUTING + * and reply direction on PRE_ROUTING. + */ + dir = CTINFO2DIR(ctinfo); + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) + { + DEBUGP("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("got beyond not touching\n"); + + datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; + + LOCK_BH(&ip_rtsp_lock); + /* Ensure the packet contains all of the marked data */ + if (!between(exp->seq + ct_rtsp_info->len, + ntohl(tcph->seq), ntohl(tcph->seq) + datalen)) + { + /* Partial retransmission? Probably a hacker. */ + if (net_ratelimit()) + { + INFOP("partial packet %u/%u in %u/%u\n", + exp->seq, ct_rtsp_info->len, ntohl(tcph->seq), ntohl(tcph->seq) + datalen); + } + UNLOCK_BH(&ip_rtsp_lock); + return NF_DROP; + } + + switch (dir) + { + case IP_CT_DIR_ORIGINAL: + rc = help_out(ct, ctinfo, exp, pskb); + break; + case IP_CT_DIR_REPLY: + rc = help_in(ct, ctinfo, exp, pskb); + break; + default: + /* oops */ + } + UNLOCK_BH(&ip_rtsp_lock); + + return rc; +} + +static struct ip_nat_helper ip_nat_rtsp_helpers[MAX_PORTS]; +static char rtsp_names[MAX_PORTS][10]; + +/* This function is intentionally _NOT_ defined as __exit */ +static void +fini(void) +{ + int i; + + for (i = 0; i < num_ports; i++) + { + DEBUGP("unregistering helper for port %d\n", ports[i]); + ip_nat_helper_unregister(&ip_nat_rtsp_helpers[i]); + } +} + +static int __init +init(void) +{ + int ret = 0; + int i; + struct ip_nat_helper* hlpr; + char* tmpname; + + printk("ip_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n"); + + if (ports[0] == 0) + { + ports[0] = RTSP_PORT; + } + + for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) + { + hlpr = &ip_nat_rtsp_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 = expected; + + tmpname = &rtsp_names[i][0]; + if (ports[i] == RTSP_PORT) + { + sprintf(tmpname, "rtsp"); + } + else + { + sprintf(tmpname, "rtsp-%d", i); + } + hlpr->name = tmpname; + + DEBUGP("registering helper for port %d: name %s\n", ports[i], hlpr->name); + ret = ip_nat_helper_register(hlpr); + + if (ret) + { + printk("ip_nat_rtsp: error registering helper for port %d\n", ports[i]); + fini(); + return 1; + } + num_ports++; + } + if (stunaddr != NULL) + { + extip = in_aton(stunaddr); + } + if (destaction != NULL) + { + if (strcmp(destaction, "auto") == 0) + { + dstact = DSTACT_AUTO; + } + if (strcmp(destaction, "strip") == 0) + { + dstact = DSTACT_STRIP; + } + if (strcmp(destaction, "none") == 0) + { + dstact = DSTACT_NONE; + } + } + return ret; +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_nat_talk.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_talk.c --- linux-2.4.26/net/ipv4/netfilter/ip_nat_talk.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_nat_talk.c Sun Apr 18 17:33:11 2004 @@ -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, { .udp = { __constant_htons(TALK_PORT) } } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { .udp = { 0xFFFF } } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + help, /* helper */ + talk_nat_expected }, /* expectfn */ + { { NULL, NULL }, + "ntalk", /* name */ + IP_NAT_HELPER_F_ALWAYS, /* flags */ + THIS_MODULE, /* module */ + { { 0, { .udp = { __constant_htons(NTALK_PORT) } } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { .udp = { 0xFFFF } } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + nhelp, /* helper */ + talk_nat_expected } /* expectfn */ + }; + +static unsigned int +talk_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + struct ip_nat_multi_range mr; + u_int32_t newdstip, newsrcip, newip; + u_int16_t port; + unsigned int ret; + + struct ip_conntrack *master = master_ct(ct); + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + + IP_NF_ASSERT(!(info->initialized & (1<master->help.exp_talk_info.port; + UNLOCK_BH(&ip_talk_lock); + + DEBUGP("ip_nat_talk_expected: dir %s at hook %s, ct %p, master %p\n", + CTINFO2DIR((*pskb)->nfct - ct->infos) == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???", + ct, master); + + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) { + /* Callee client -> caller server */ +#ifdef IP_NAT_TALK_DEBUG + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + + DEBUGP("ip_nat_talk_expected: UDP %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(udph->source), + NIPQUAD(iph->daddr), ntohs(udph->dest)); +#endif + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + DEBUGP("ip_nat_talk_expected: callee client -> caller server, newsrc: %u.%u.%u.%u, newdst: %u.%u.%u.%u\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } else { + /* Callee client -> caller client */ +#ifdef IP_NAT_TALK_DEBUG + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + + DEBUGP("ip_nat_talk_expected: TCP %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), + NIPQUAD(iph->daddr), ntohs(tcph->dest)); +#endif + newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + DEBUGP("ip_nat_talk_expected: callee client -> caller client, newsrc: %u.%u.%u.%u, newdst: %u.%u.%u.%u\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) + newip = newsrcip; + else + newip = newdstip; + + DEBUGP("ip_nat_talk_expected: IP to %u.%u.%u.%u, port %u\n", NIPQUAD(newip), ntohs(port)); + + mr.rangesize = 1; + /* We don't want to manip the per-protocol, just the IPs... */ + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + /* ... unless we're doing a MANIP_DST, in which case, make + sure we map to the correct port */ + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { + mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + mr.range[0].min = mr.range[0].max + = ((union ip_conntrack_manip_proto) + { .udp = { port } }); + } + ret = ip_nat_setup_info(ct, &mr, hooknum); + + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) { + DEBUGP("talk_expected: setting NAT helper for %p\n", ct); + /* NAT expectfn called with ip_nat_lock write-locked */ + info->helper = &talk_helpers[htons(port) - TALK_PORT]; + } + return ret; +} + +static int __init init(void) +{ + int ret = 0; + + if (talk > 0) { + ret = ip_nat_helper_register(&talk_helpers[0]); + + if (ret != 0) + return ret; + } + if (ntalk > 0 || ntalk2 > 0) { + ret = ip_nat_helper_register(&talk_helpers[1]); + + if (ret != 0 && talk > 0) + ip_nat_helper_unregister(&talk_helpers[0]); + } + return ret; +} + +static void __exit fini(void) +{ + if (talk > 0) + ip_nat_helper_unregister(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_nat_helper_unregister(&talk_helpers[1]); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_pool.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_pool.c --- linux-2.4.26/net/ipv4/netfilter/ip_pool.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_pool.c Sun Apr 18 17:33:08 2004 @@ -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; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASSERT_READ_LOCK(x) /* dont use that */ +#define ASSERT_WRITE_LOCK(x) +#include +#include + +static struct list_head set_type_list; /* all registred set types */ +struct ip_set **ip_set_list; /* all individual sets */ +static rwlock_t list_lock = RW_LOCK_UNLOCKED; /* protects both set_type_list and ip_set_list */ +static unsigned int max_sets = 0; /* max number of sets, */ + +/* Arrgh */ +#ifdef MODULE +#define __MOD_INC(foo) __MOD_INC_USE_COUNT(foo) +#define __MOD_DEC(foo) __MOD_DEC_USE_COUNT(foo) +#else +#define __MOD_INC(foo) do { } while (0) +#define __MOD_DEC(foo) do { } while (0) +#endif + +#define NOT_IN_CHILD_SET(fn,args...) \ + !*private \ + || !(*private)->childsets \ + || (set->type[i]->fn(*private,##args) < 0) + +static struct ip_set_private ** +ip_set_find_private(struct ip_set *set, + struct ip_set_private **private, + ip_set_ip_t *ip, + u_int8_t level) +{ + int i; + ip_set_ip_t id; + + for (i = 0; i < level; i++) { + if (NOT_IN_CHILD_SET(matchip, ip[i], &id)) + return NULL; + private = &(*private)->childsets[id]; + } + DP("id: %i private: %p %p", id, private, *private); + return private; +} + +/* Destroy function for the private part of the (child)sets. + * Must be called without holding any locks. + */ +static void +ip_set_destroy_private(struct ip_set *set, + struct ip_set_private **private, + u_int8_t level) +{ + int i; + + DP("set %p private %p %p %p", set, private, *private, (*private)->childsets); + if ((*private)->childsets) { + for (i = 0; i < set->type[level]->sizeid(*private); i++) + if ((*private)->childsets[i]) { + DP("%i -> %p", i, (*private)->childsets[i]); + ip_set_destroy_private(set, + &(*private)->childsets[i], + level + 1); + } + vfree((*private)->childsets); + } + + set->type[level]->destroy(private); + DP("%p %p", private, *private); +} + +static void ip_set_flush_private(struct ip_set *set, + struct ip_set_private *private, + u_int8_t level, + u_int8_t childsets) +{ + int i; + + if (childsets && private->childsets) + for (i = 0; i < set->type[level]->sizeid(private); i++) + if (private->childsets[i]) + ip_set_flush_private(set, + private->childsets[i], + level + 1, + childsets); + + set->type[level]->flush(private); + +} + +/* ip_set_flush() - flush data in a set + */ +static int ip_set_flush(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level, + u_int8_t childsets) +{ + int res = 0; + struct ip_set_private **private; + + write_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + + private = ip_set_find_private(set, &set->private, ip, level); + + if (private) + ip_set_flush_private(set, *private, level, childsets); + + unlock: + write_unlock_bh(&set->lock); + return res; +} + +int +ip_set_testip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level) +{ + struct ip_set_private **private = &set->private; + ip_set_ip_t id; + int i, res = 0; + + read_lock_bh(&set->lock); + if (set->levels < ip_level || set->subref) + goto unlock; + + for (i = 0; i < set_level; i++) { + if (NOT_IN_CHILD_SET(testip_kernel, skb, + flags[i] | set->type[i]->typecode, &id)) + goto unlock; + DP("id: %i private: %p", id, *private); + private = &(*private)->childsets[id]; + } + for (i = set_level; private && *private && i < ip_level; i++) { + if (set->type[i]->testip_kernel(*private, skb, + flags[i] | set->type[i]->typecode, &id) <= 0) + goto unlock; + private = (*private)->childsets + ? &(*private)->childsets[id] : NULL; + } + res = 1; + unlock: + read_unlock_bh(&set->lock); + return res; +} + +void +ip_set_addip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level) +{ + struct ip_set_private **private = &set->private; + ip_set_ip_t id; + int i, res; + + write_lock_bh(&set->lock); + if (set->levels < ip_level || set->subref) { + write_unlock_bh(&set->lock); + return; + } + for (i = 0; i < set_level; i++) { + if (NOT_IN_CHILD_SET(testip_kernel, skb, + flags[i] | set->type[i]->typecode, &id)) { + write_unlock_bh(&set->lock); + return; + } + private = &(*private)->childsets[id]; + } + for (i = set_level; private && *private && i < ip_level; i++) { + res = set->type[i]->addip_kernel(*private, skb, + flags[i] | set->type[i]->typecode, &id); + if (!(res == 0 || res == -EEXIST)) { + write_unlock_bh(&set->lock); + return; + } + private = (*private)->childsets + ? &(*private)->childsets[id] : NULL; + } + write_unlock_bh(&set->lock); +} + +void +ip_set_delip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level) +{ + struct ip_set_private **private = &set->private; + ip_set_ip_t id; + int i, res; + + write_lock_bh(&set->lock); + if (set->levels < ip_level || set->subref) { + write_unlock_bh(&set->lock); + return; + } + for (i = 0; i < set_level; i++) { + if (NOT_IN_CHILD_SET(testip_kernel, skb, + flags[i] | set->type[i]->typecode, &id)) { + write_unlock_bh(&set->lock); + return; + } + private = &(*private)->childsets[id]; + } + for (i = set_level; private && *private && i < ip_level; i++) { + res = set->type[i]->delip_kernel(*private, skb, + flags[i] | set->type[i]->typecode, &id); + if (!(res == 0 || res == -EEXIST)) { + write_unlock_bh(&set->lock); + return; + } + private = (*private)->childsets + ? &(*private)->childsets[id] : NULL; + } + write_unlock_bh(&set->lock); +} + +static int +ip_set_addip(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level, + const void *data, + size_t size) +{ + struct ip_set_private **private; + ip_set_ip_t id; + int res = 0; + + DP("%s %i %d", set->name, level, size); + write_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + private = ip_set_find_private(set, &set->private, ip, level); + DP("%s %i %d", set->name, level, size); + while (level <= set->levels && size) { + DP("%s %i %d", set->name, level, size); + if (!(private && *private)) { + res = -ENOENT; + goto unlock; + } + if (size < set->type[level]->reqsize) { + res = -EINVAL; + goto unlock; + } + res = set->type[level]->addip(*private, data, + set->type[level]->reqsize, &id); + if (!(res == 0 || res == -EEXIST)) + goto unlock; + private = (*private)->childsets ? &(*private)->childsets[id] : NULL; + data += set->type[level]->reqsize; + size -= set->type[level++]->reqsize; + } + if (size) + res = -EINVAL; + unlock: + write_unlock_bh(&set->lock); + return res; +} + +static int +ip_set_delip(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level, + const void *data, + size_t size) +{ + struct ip_set_private **private; + ip_set_ip_t id; + int res = 0; + + write_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + private = ip_set_find_private(set, &set->private, ip, level); + while (level <= set->levels && size) { + if (!(private && *private)) { + res = -ENOENT; + goto unlock; + } + if (size < set->type[level]->reqsize) { + res = -EINVAL; + goto unlock; + } + res = set->type[level]->delip(*private, data, + set->type[level]->reqsize, &id); + if (!(res == 0 || res == -EEXIST)) + goto unlock; + private = (*private)->childsets ? &(*private)->childsets[id] : NULL; + data += set->type[level]->reqsize; + size -= set->type[level++]->reqsize; + } + if (size) + res = -EINVAL; + unlock: + write_unlock_bh(&set->lock); + return res; +} + +static int +ip_set_testip(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level, + const void *data, + size_t size) +{ + struct ip_set_private **private; + ip_set_ip_t id; + int res = 0; + + write_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + private = ip_set_find_private(set, &set->private, ip, level); + while (level <= set->levels && size) { + if (!(private && *private)) { + res = -ENOENT; + goto unlock; + } + if (size < set->type[level]->reqsize) { + res = -EINVAL; + goto unlock; + } + res = set->type[level]->testip(*private, data, + set->type[level]->reqsize, &id); + DP("level: %i res: %i", level, res); + if (res <= 0) + goto unlock; + private = (*private)->childsets ? &(*private)->childsets[id] : NULL; + data += set->type[level]->reqsize; + size -= set->type[level++]->reqsize; + } + if (size) + res = -EINVAL; + unlock: + write_unlock_bh(&set->lock); + return (res > 0); +} + +static inline int +set_type_equal(const struct ip_set_type *set_type, const char *str2) +{ + DP("'%s' vs. '%s'", set_type->typename, str2); + return !strncmp(set_type->typename, str2, IP_SET_MAXNAMELEN - 1); +} + +/* + * Always use find_setfoo() under the &list_lock. + */ +static inline struct ip_set_type *find_set_type(const char name[IP_SET_MAXNAMELEN]) +{ + return LIST_FIND(&set_type_list, + set_type_equal, + struct ip_set_type *, + name); +} + +int ip_set_register_set_type(struct ip_set_type *set_type) +{ + if (set_type->protocol_version != IP_SET_PROTOCOL_VERSION) { + ip_set_printk("'%s' uses wrong protocol version %u (want %u)", + set_type->typename, + set_type->protocol_version, + IP_SET_PROTOCOL_VERSION); + return -EINVAL; + } + + write_lock_bh(&list_lock); + if (find_set_type(set_type->typename)) { + /* Duplicate! */ + write_unlock_bh(&list_lock); + ip_set_printk("'%s' already registered!", + set_type->typename); + return -EINVAL; + } + MOD_INC_USE_COUNT; + list_append(&set_type_list, set_type); + write_unlock_bh(&list_lock); + DP("'%s' registered.", set_type->typename); + return 0; +} + +void ip_set_unregister_set_type(struct ip_set_type *set_type) +{ + write_lock_bh(&list_lock); + if (!find_set_type(set_type->typename)) { + ip_set_printk("'%s' not registered?", + set_type->typename); + write_unlock_bh(&list_lock); + return; + } + LIST_DELETE(&set_type_list, set_type); + write_unlock_bh(&list_lock); + MOD_DEC_USE_COUNT; + + DP("'%s' unregistered.", set_type->typename); +} + +/* Create the private part of a (child)set. + * Must be called without holding any locks. + */ +static int +ip_set_create_private(struct ip_set_type *set_type, + struct ip_set_private **private, + const void *data, + size_t size, + u_int8_t childsets) +{ + int res = 0; + int newbytes; + + DP("%s %p %p %i", set_type->typename, private, *private, childsets); + + if (*private) + printk("%p: %p as private already occupied", private, *private); + + /* Call the set_type initializer. */ + res = set_type->create(private, data, size); + if (res != 0) + return res; + + if (!childsets) { + (*private)->childsets = NULL; + return res; + } + + /* Create room for subsets */ + newbytes = set_type->sizeid(*private) * sizeof(struct ip_set_private *); + DP("%s (%p) %i", set_type->typename, *private, newbytes); + (*private)->childsets = vmalloc(newbytes); \ + if (!(*private)->childsets) { + set_type->destroy(private); + return -ENOMEM; + } + DP("%s %p %p %p", set_type->typename, private, *private, (*private)->childsets); + memset((*private)->childsets, 0, newbytes); + return res; +} + +static int +ip_set_create_childset(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level, + u_int8_t childsets, + const void *data, + size_t size) +{ + struct ip_set_private **private = &set->private; + ip_set_ip_t id; + int res; + + DP("%s (%i %i)", set->name, level, childsets); + write_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + if (level > 1) + private = ip_set_find_private(set, private, ip, level - 1); + DP("%s (%i %i) %p %p", set->name, level, childsets, private, *private); + if (!(private && *private && (*private)->childsets)) { + res = -ENOENT; + goto unlock; + } + DP("%s (%i %i) %p %p", set->name, level, childsets, private, *private); + set->type[level - 1]->matchip(*private, ip[level - 1], &id); + DP("%s (%i %i) %p %p %i", set->name, level, childsets, private, *private, id); + if (id < 0) { + res = -ENOENT; + goto unlock; + } + if ((*private)->childsets[id]) { + res = -EEXIST; + goto unlock; + } + set->subref++; + write_unlock_bh(&set->lock); + + /* Without holding any locks, create private part. */ + res = ip_set_create_private(set->type[level], + &(*private)->childsets[id], + data, size, childsets); + + write_lock_bh(&set->lock); + set->subref--; + unlock: + DP("%s (%p %p) res=%i", set->name, private, *private, res); + write_unlock_bh(&set->lock); + return res; +} + +static int +ip_set_create(const char name[IP_SET_MAXNAMELEN], + char typename[IP_SET_LEVELS][IP_SET_MAXNAMELEN], + u_int8_t level, + const void *data, + size_t size) +{ + int i, id, res = 0; + struct ip_set *set; + + DP("%s (%i): %s", typename[0], level, name); + /* + * First, and without any locks, allocate and initialize + * a normal base set structure. + */ + set = kmalloc(sizeof(struct ip_set), GFP_KERNEL); + if (!set) + return -ENOMEM; + set->lock = RW_LOCK_UNLOCKED; + strncpy(set->name, name, IP_SET_MAXNAMELEN); + set->name[IP_SET_MAXNAMELEN - 1] = '\0'; + set->ref = 0; + set->subref = 0; + set->levels = level; + set->private = NULL; + + /* + * Next, take the &list_lock, check that we know the type, + * and take a reference on the type, to make sure it + * stays available while constructing our new set. + * + * After referencing the type, we drop the &list_lock, + * and let the new set construction run without locks. + */ + write_lock_bh(&list_lock); + for (i = 0; i < level; i++) { + set->type[i] = find_set_type(typename[i]); + if (set->type[i] == NULL) { + /* FIXME: try loading the module */ + write_unlock_bh(&list_lock); + ip_set_printk("no set type '%s', set '%s' not created", + typename[i], name); + kfree(set); + return -EINVAL; + } + } + for (i = 0; i < level; i++) + __MOD_INC(set->type[i]->me); + write_unlock_bh(&list_lock); + + /* + * Without holding any locks, create private part. + */ + res = ip_set_create_private(set->type[0], + &set->private, + data, size, level - 1); + if (res != 0) { + for (i = 0; i <= level; i++) + __MOD_DEC(set->type[i]->me); + kfree(set); + return res; + } + + /* BTW, res==0 here. */ + + /* + * Here, we have a valid, constructed set. &list_lock again, + * and check that it is not already in ip_set_list. + */ + write_lock_bh(&list_lock); + id = -1; + for (i = 0; i < max_sets; i++) { + if (ip_set_list[i] != NULL + && strncmp(ip_set_list[i]->name, set->name, + IP_SET_MAXNAMELEN - 1) == 0) { + res = -EEXIST; + goto cleanup; + } else if (id < 0 && ip_set_list[i] == NULL) + id = i; + } + if (id < 0) { + /* No free slot remained */ + res = -ERANGE; + goto cleanup; + } + /* + * Finally! Append our shiny new set into the list, and be done. + */ + DP("create: '%s' created with id %i!", set->name, id); + ip_set_list[id] = set; + write_unlock_bh(&list_lock); + return res; + + cleanup: + write_unlock_bh(&list_lock); + ip_set_destroy_private(set, &set->private, 0); + for (i = 0; i < level; i++) + __MOD_DEC(set->type[i]->me); + kfree(set); + return res; +} + +static int ip_set_destroy(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level) +{ + struct ip_set_private **private; + int i, res = 0; + + write_lock_bh(&list_lock); + /* there is no race, here. ->ref modification always happens + * under &list_lock. Fine. + */ + if (level == 0) { + /* one ref from caller */ + if (set->ref > 1 || set->subref) { + res = -EBUSY; + goto unlock; + } + + for (i = 0; i < max_sets; i++) + if (ip_set_list[i] == set) { + ip_set_list[i] = NULL; + break; + } + write_unlock_bh(&list_lock); + + ip_set_destroy_private(set, &set->private, 0); + for (i = 0; i < set->levels; i++) + __MOD_DEC(set->type[i]->me); + kfree(set); + return res; + } + + private = ip_set_find_private(set, &set->private, + ip, level); + + if (private && *private) { + if (set->subref) { + res = -EBUSY; + goto unlock; + } + set->subref++; + write_unlock_bh(&list_lock); + + DP("%p %p", private, *private); + ip_set_destroy_private(set, private, level); + DP("%p %p", private, *private); + + write_lock_bh(&list_lock); + set->subref--; + } else + res = -ENOENT; + + unlock: + write_unlock_bh(&list_lock); + return res; +} + +/* + * Find set by name, reference it once. The reference makes sure the + * thing pointed to, does not go away under our feet. Drop the reference + * later, using ip_set_put(). + */ +struct ip_set *ip_set_get_byname(const char name[IP_SET_MAXNAMELEN], + int *id) +{ + struct ip_set *set = NULL; + int i; + + read_lock_bh(&list_lock); + for (i = 0; i < max_sets; i++) { + set = ip_set_list[i]; + if (set != NULL + && strncmp(set->name, name, IP_SET_MAXNAMELEN - 1) == 0) { + set->ref++; + *id = i; + break; + } + } + read_unlock_bh(&list_lock); + return set; +} + +/* + * Find set by id, reference it once. The reference makes sure the + * thing pointed to, does not go away under our feet. Drop the reference + * later, using ip_set_put(). + */ +struct ip_set *ip_set_get_byid(int id) +{ + struct ip_set *set; + + if (id < 0 || id >= max_sets) + return NULL; + + write_lock_bh(&list_lock); + set = ip_set_list[id];; + if (set) + set->ref++; + write_unlock_bh(&list_lock); + return set; +} + +/* + * If the given set pointer points to a valid set, decrement + * reference count by 1. The caller shall not assume the pointer + * to be valid, after calling this function. + */ +void ip_set_put(struct ip_set *set) +{ + write_lock_bh(&list_lock); + if (set) + set->ref--; + write_unlock_bh(&list_lock); +} + +static int ip_set_rename(struct ip_set *set, const char *name) +{ + int i, res = 0; + + write_lock_bh(&list_lock); + for (i = 0; i < max_sets; i++) { + if (ip_set_list[i] != NULL + && strncmp(ip_set_list[i]->name, + name, + IP_SET_MAXNAMELEN - 1) == 0) { + res = -EEXIST; + goto unlock; + } + } + strncpy(set->name, name, IP_SET_MAXNAMELEN); + set->name[IP_SET_MAXNAMELEN - 1] = '\0'; + unlock: + write_unlock_bh(&list_lock); + return res; +} + +static int ip_set_swap(struct ip_set *from, struct ip_set *to) +{ + char from_name[IP_SET_MAXNAMELEN]; + unsigned from_ref; + int i, res = 0; + int from_id = -1, to_id = -1; + + write_lock_bh(&list_lock); + for (i = 0; i < max_sets && (from_id < 0 || to_id < 0); i++) { + if (ip_set_list[i] == from) + from_id = i; + if (ip_set_list[i] == to) + to_id = i; + } + /* We must have got both sets: we hold refcounts against them! */ + if (from_id < 0 || to_id < 0) { + res = -EINVAL; + goto unlock; + } + + strncpy(from_name, from->name, IP_SET_MAXNAMELEN); + from_ref = from->ref; + + ip_set_list[from_id] = to; + ip_set_list[to_id] = from; + + strncpy(from->name, to->name, IP_SET_MAXNAMELEN); + from->ref = to->ref; + strncpy(to->name, from_name, IP_SET_MAXNAMELEN); + to->ref = from_ref; + unlock: + write_unlock_bh(&list_lock); + return res; +} + +size_t ip_set_listing_size(void) +{ + size_t size = 0; + int id; + + read_lock_bh(&list_lock); + for (id = 0; id < max_sets; id++) { + if (ip_set_list[id] != NULL) + size += sizeof(struct ip_set_req_listing); + } + read_unlock_bh(&list_lock); + + return size; +} + +int ip_set_listing(void *data, int *len) +{ + int used = 0; + int res = 0; /* All OK */ + int i, id; + struct ip_set *set; + struct ip_set_req_listing *header = data; + + read_lock_bh(&list_lock); + for (id = 0; id < max_sets; id++) { + if (ip_set_list[id] == NULL) + continue; + + /* Pointer to our header */ + header = (struct ip_set_req_listing *) (data + used); + + DP("used before= %d %p %p %p", used, header, data, + data + used); + + /* Get and ensure header size */ + if (used + sizeof(struct ip_set_req_listing) > *len) + goto not_enough_mem; + + set = ip_set_list[id]; + + /* Fill with data */ + strncpy(header->name, set->name, IP_SET_MAXNAMELEN - 1); + for (i = 0; i < set->levels; i++) + strncpy(header->typename[i], set->type[i]->typename, + IP_SET_MAXNAMELEN - 1); + header->levels = set->levels; + header->ref = set->ref; + header->id = id; + + used += sizeof(struct ip_set_req_listing); + DP("used after= %d", used); + } + *len = used; /* How much did we use? */ + goto unlock_and_return; + + not_enough_mem: + DP("not enough mem, try again"); + res = -ENOMEM; + + unlock_and_return: + read_unlock_bh(&list_lock); + return res; +} + +int ip_set_list_size(struct ip_set * set, + ip_set_ip_t *ip, + unsigned level, + size_t *size, + unsigned op) +{ + int res = 0; /* OK */ + struct ip_set_private **private; + + DP("%d %s %d", op, set->name, level); + read_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + private = ip_set_find_private(set, &set->private, ip, level); + if (!(private && *private)) { + res = -ENOENT; + goto unlock; + } + switch (op) { + case IP_SET_OP_LIST_HEADER_SIZE: + *size = set->type[level]->list_header_size(*private); + break; + case IP_SET_OP_LIST_MEMBERS_SIZE: + *size = set->type[level]->list_members_size(*private); + break; + case IP_SET_OP_LIST_CHILDSETS_SIZE: + *size = (*private)->childsets == NULL ? 0 + : bitmap_bytes(0, set->type[level]->sizeid(*private) - 1); + break; + default: + res = -EINVAL; + } + unlock: + read_unlock_bh(&set->lock); + DP("%d %s %d: %u", op, set->name, level, *size); + + return res; +} + +static void list_childsets(const struct ip_set_private *private, + void *data, + ip_set_ip_t sizeid) +{ + ip_set_ip_t id; + + memset(data, 0, bitmap_bytes(0, sizeid - 1)); + + if (private->childsets == NULL) + return; + + for (id = 0; id < sizeid; id++) + if (private->childsets[id] != NULL) + set_bit(id, data); +} + +int ip_set_list_data(struct ip_set *set, + ip_set_ip_t *ip, + unsigned level, + void *data, + int *len, + unsigned op) +{ + int res = 0; /* All OK */ + size_t need; + struct ip_set_private **private; + void (*datafn)(const struct ip_set_private *, void *); + + read_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + private = ip_set_find_private(set, &set->private, ip, level); + if (!(private && *private)) { + res = -ENOENT; + goto unlock; + } + switch (op) { + case IP_SET_OP_LIST_HEADER: + need = set->type[level]->list_header_size(*private); + datafn = set->type[level]->list_header; + break; + case IP_SET_OP_LIST_MEMBERS: + need = set->type[level]->list_members_size(*private); + datafn = set->type[level]->list_members; + break; + case IP_SET_OP_LIST_CHILDSETS: + if ((*private)->childsets == NULL) { + res = -ENOENT; + goto unlock; + } + need = bitmap_bytes(0, set->type[level]->sizeid(*private) - 1); + datafn = NULL; + break; + default: + res = -EINVAL; + goto unlock; + } + if (need > *len) { + res = -ENOMEM; + goto unlock; + } + *len = need; + if (datafn) + datafn(*private, data); + else + list_childsets(*private, data, set->type[level]->sizeid(*private)); + + unlock: + read_unlock_bh(&set->lock); + return res; +} + +static int +ip_set_sockfn_set(struct sock *sk, int optval, void *user, unsigned int len) +{ + void *data; + int res = 0; /* Assume OK */ + struct ip_set_req_base *req_base; + struct ip_set_req_std *req_std; + struct ip_set *set = NULL; + + DP("optval=%d, user=%p, len=%d", optval, user, len); + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (optval != SO_IP_SET) + return -EBADF; + if (len < sizeof(struct ip_set_req_base)) { + ip_set_printk("short userdata (want >=%d, got %d)", + sizeof(struct ip_set_req_base), len); + return -EINVAL; + } + data = vmalloc(len); + if (!data) { + DP("out of mem for %d bytes", len); + return -ENOMEM; + } + if (copy_from_user(data, user, len) != 0) { + res = -EFAULT; + goto done; + } + + req_base = (struct ip_set_req_base *) data; + + DP("op=%x id='%x'", req_base->op, req_base->id); + + /* Handle set creation first - no incoming set specified */ + + if (req_base->op == IP_SET_OP_CREATE) { + struct ip_set_req_create *req_create + = (struct ip_set_req_create *) data; + int i; + + if (len < sizeof(struct ip_set_req_create)) { + ip_set_printk("short CREATE data (want >%d, got %d)", + sizeof(struct ip_set_req_create), len); + res = -EINVAL; + goto done; + } + if (req_create->levels > IP_SET_LEVELS) { + ip_set_printk("set level %d too deep (max %d)", + req_create->levels, IP_SET_LEVELS); + res = -EINVAL; + goto done; + } + req_create->name[IP_SET_MAXNAMELEN - 1] = '\0'; + for (i = 0; i < req_create->levels; i++) + req_create->typename[i][IP_SET_MAXNAMELEN - 1] = '\0'; + res = ip_set_create(req_create->name, + req_create->typename, + req_create->levels, + data + sizeof(struct ip_set_req_create), + len - sizeof(struct ip_set_req_create)); + goto done; + } + + /* All remaining requests want a set by id. + * We take a proper reference here, and drop it after processing. + * From hereon, code goes to '*put_set', not to 'done'. + */ + + set = ip_set_get_byid(req_base->id); + if (set == NULL) { + res = -ESRCH; + goto done; + } + + DP("set %s (%d) (%u)", set->name, req_base->id, set->ref); + /* Simple requests: no subsets */ + switch (req_base->op) { + case IP_SET_OP_RENAME:{ + struct ip_set_req_rename *req_rename + = (struct ip_set_req_rename *) data; + + if (len != sizeof(struct ip_set_req_rename)) { + ip_set_printk("short RENAME data (want >%d, got %d)", + sizeof(struct ip_set_req_rename), len); + res = -EINVAL; + goto put_set; + } + + res = ip_set_rename(set, req_rename->newname); + goto put_set; + } + + case IP_SET_OP_SWAP:{ + struct ip_set_req_swap *req_swap + = (struct ip_set_req_swap *) data; + struct ip_set *to; + + if (len != sizeof(struct ip_set_req_swap)) { + + ip_set_printk("short SWAP data (want >%d, got %d)", + sizeof(struct ip_set_req_swap), len); + res = -EINVAL; + goto put_set; + } + + to = ip_set_get_byid(req_swap->to); + if (to == NULL) { + res = -ESRCH; + goto put_set; + } + res = ip_set_swap(set, to); + ip_set_put(to); + goto put_set; + } + default: + ; /* Requests with possible subsets: fall trough. */ + } + + req_std = (struct ip_set_req_std *) data; + if (len < sizeof(struct ip_set_req_std)) { + ip_set_printk("short data in std request (want >%d, got %d)", + sizeof(struct ip_set_req_std), len); + res = -EINVAL; + goto put_set; + } else if (req_std->level >= set->levels) { + res = -EINVAL; + goto put_set; + } + + switch (req_base->op) { + case IP_SET_OP_ADD_IP:{ + res = ip_set_addip(set, + req_std->ip, req_std->level, + data + sizeof(struct ip_set_req_std), + len - sizeof(struct ip_set_req_std)); + goto put_set; + } + case IP_SET_OP_DEL_IP:{ + res = ip_set_delip(set, + req_std->ip, req_std->level, + data + sizeof(struct ip_set_req_std), + len - sizeof(struct ip_set_req_std)); + goto put_set; + } + case IP_SET_OP_DESTROY:{ + res = ip_set_destroy(set, req_std->ip, req_std->level); + if (req_std->level == 0 && res == 0) + goto done; /* destroyed: no ip_set_put */ + goto put_set; + } + case IP_SET_OP_FLUSH:{ + struct ip_set_req_sub *req_sub = + (struct ip_set_req_sub *) data; + + if (len < sizeof(struct ip_set_req_sub)) { + ip_set_printk("short data in flush request (want >%d, got %d)", + sizeof(struct ip_set_req_sub), len); + res = -EINVAL; + goto put_set; + } + res = ip_set_flush(set, req_sub->ip, req_sub->level, req_sub->childsets); + goto put_set; + } + case IP_SET_OP_CREATE_CHILD:{ + struct ip_set_req_sub *req_sub + = (struct ip_set_req_sub *) data; + + if (len < sizeof(struct ip_set_req_sub)) { + ip_set_printk("short CREATE_CHILD data (want >%d, got %d)", + sizeof(struct ip_set_req_sub), len); + res = -EINVAL; + goto put_set; + } + if (req_sub->level < 1) { + /* No entry supplied? */ + res = -EINVAL; + goto put_set; + } + if (((req_sub->level >= set->levels - 1) && req_sub->childsets)) { + /* No room for subsets to be created. */ + res = -ERANGE; + goto put_set; + } + res = ip_set_create_childset(set, + req_sub->ip, + req_sub->level, + req_sub->childsets, + data + sizeof(struct ip_set_req_sub), + len - sizeof(struct ip_set_req_sub)); + goto put_set; + } + default:{ + DP("unknown op %d", req_base->op); + ip_set_printk("obsolete - upgrade your ipset(8) utility."); + res = -EINVAL; + } + } /* end of switch(op) */ + + put_set: + if (set) + ip_set_put(set); + done: + vfree(data); + if (res > 0) + res = 0; + return res; +} + +static int +ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len) +{ + int res = 0; + struct ip_set_req_base *req_base; + struct ip_set_req_std *req_std; + struct ip_set *set = NULL; + void *data; + int copylen = *len; + + DP("optval=%d, user=%p, len=%d", optval, user, *len); + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (optval != SO_IP_SET) + return -EBADF; + if (*len < sizeof(struct ip_set_req_base)) { + ip_set_printk("short userdata (want >=%d, got %d)", + sizeof(struct ip_set_req_base), *len); + return -EINVAL; + } + data = vmalloc(*len); + if (!data) { + DP("out of mem for %d bytes", *len); + return -ENOMEM; + } + if (copy_from_user(data, user, *len) != 0) { + res = -EFAULT; + goto done; + } + + req_base = (struct ip_set_req_base *) data; + + DP("op=%x id='%x'", req_base->op, req_base->id); + + /* Requests without a named set. */ + switch (req_base->op) { + case IP_SET_OP_VERSION:{ + struct ip_set_req_version *req_version = + (struct ip_set_req_version *) data; + + if (*len != sizeof(struct ip_set_req_version)) { + ip_set_printk("short VERSION (want >=%d, got %d)", + sizeof(struct ip_set_req_version), + *len); + res = -EINVAL; + goto done; + } + + req_version->version = IP_SET_PROTOCOL_VERSION; + res = copy_to_user(user, req_version, + sizeof(struct ip_set_req_version)); + goto done; + } + case IP_SET_OP_LISTING_SIZE:{ + struct ip_set_req_listing_size *req_list = + (struct ip_set_req_listing_size *) data; + + DP("IP_SET_OP_LISTING_SIZE"); + + if (*len != sizeof(struct ip_set_req_listing_size)) { + ip_set_printk("short LISTING_SIZE (want >=%d, got %d)", + sizeof(struct ip_set_req_listing_size), + *len); + res = -EINVAL; + goto done; + } + + req_list->size = ip_set_listing_size(); + DP("req_list->size = %d", req_list->size); + res = copy_to_user(user, req_list, + sizeof(struct ip_set_req_listing_size)); + goto done; + } + case IP_SET_OP_LISTING:{ + DP("LISTING before len=%d", *len); + res = ip_set_listing(data, len); + DP("LISTING done len=%d", *len); + if (res < 0) + goto done; /* Error */ + + res = copy_to_user(user, data, *len); /* Only copy the mem used */ + goto done; + } + default: + ; /* Requests with named set: fall trought */ + } + + /* Special cases: GETSET_BYNAME/BYID */ + switch (req_base->op) { + case IP_SET_OP_GETSET_BYNAME: { + struct ip_set_req_get *req_get + = (struct ip_set_req_get *) data; + + if (*len != sizeof(struct ip_set_req_get)) { + ip_set_printk("short _BYNAME (want >=%d, got %d)", + sizeof(struct ip_set_req_get), *len); + res = -EINVAL; + goto done; + } + req_get->name[IP_SET_MAXNAMELEN - 1] = '\0'; + req_get->id = -1; + set = ip_set_get_byname(req_get->name, &req_get->id); + if (set) { + req_get->ref = set->ref - 1; + ip_set_put(set); + } + res = copy_to_user(user, data, copylen); + goto done; + } + case IP_SET_OP_GETSET_BYID: { + struct ip_set_req_get *req_get + = (struct ip_set_req_get *) data; + + if (*len != sizeof(struct ip_set_req_get)) { + ip_set_printk("short _BYID (want >=%d, got %d)", + sizeof(struct ip_set_req_get), *len); + res = -EINVAL; + goto done; + } + set = ip_set_get_byid(req_get->id); + if (set) { + req_get->ref = set->ref - 1; + strncpy(req_get->name, set->name, IP_SET_MAXNAMELEN); + ip_set_put(set); + } else + req_get->id = -1; + res = copy_to_user(user, data, copylen); + goto done; + } + default: + ; /* Requests with set id: fall trought */ + } + + /* Requests with set id: */ + if (req_base->id < 0 || req_base->id >= max_sets) { + res = -EINVAL; + goto done; + } + set = ip_set_get_byid(req_base->id); /* Reference lock */ + if (!set) { + res = -ENOENT; + goto done; + } + + DP("set %s (%d) (%u)", set->name, req_base->id, set->ref); + req_std = (struct ip_set_req_std *) data; + if (*len < sizeof(struct ip_set_req_std)) { + ip_set_printk("short data in std request (want >%d, got %d)", + sizeof(struct ip_set_req_std), *len); + goto put_inval; + } else if (req_std->level >= set->levels) { + res = -ERANGE; + goto put_set; + } + + switch (req_base->op) { + case IP_SET_OP_TEST_IP:{ + struct ip_set_req_test *req_test = + (struct ip_set_req_test *) data; + + if (*len < sizeof(struct ip_set_req_test)) { + ip_set_printk("short data in testip request (want >%d, got %d)", + sizeof(struct ip_set_req_test), *len); + res = -EINVAL; + goto put_set; + } + req_test->reply = ip_set_testip(set, + req_test->ip, + req_test->level, + data + sizeof(struct ip_set_req_test), + *len - sizeof(struct ip_set_req_test)); + + DP("test result: %i", req_test->reply); + *len = copylen = sizeof(struct ip_set_req_test); + goto put_copy; + } + case IP_SET_OP_LIST_HEADER_SIZE: + case IP_SET_OP_LIST_MEMBERS_SIZE: + case IP_SET_OP_LIST_CHILDSETS_SIZE: { + struct ip_set_req_list *req_list = + (struct ip_set_req_list *) data; + + if (*len != sizeof(struct ip_set_req_list)) { + ip_set_printk("short LIST (want >=%d, got %d)", + sizeof(struct ip_set_req_list), + *len); + goto put_inval; + } + res = ip_set_list_size(set, + req_list->ip, + req_list->level, + &req_list->size, + req_base->op); + DP("SIZEfoo size=%d", req_list->size); + if (res < 0) + goto put_set; /* Error */ + goto put_copy; + } + case IP_SET_OP_LIST_HEADER: + case IP_SET_OP_LIST_MEMBERS: + case IP_SET_OP_LIST_CHILDSETS:{ + DP("LISTfoo before len=%d", *len); + res = ip_set_list_data(set, + req_std->ip, + req_std->level, + data, + len, + req_base->op); + DP("LISTfoo done len=%d", *len); + + if (res < 0) + goto put_set; /* Error */ + + copylen = *len; /* Only copy the mem used */ + goto put_copy; + } + default:{ + DP("unknown op %d", req_base->op); + ip_set_printk("obsolete - upgrade your ipset(8) utility."); + goto put_inval; + } + } /* end of switch(op) */ + + put_copy: + ip_set_put(set); + DP("set %s (%u)", set->name, set->ref); + res = copy_to_user(user, data, copylen); + goto done; + put_inval: + res = -EINVAL; + put_set: + ip_set_put(set); + DP("set %s (%u)", set->name, set->ref); + done: + vfree(data); + if (res > 0) + res = 0; + DP("final result %d", res); + return res; +} + +static struct nf_sockopt_ops so_set = { + .pf = PF_INET, + .set_optmin = SO_IP_SET, + .set_optmax = SO_IP_SET + 1, + .set = &ip_set_sockfn_set, + .get_optmin = SO_IP_SET, + .get_optmax = SO_IP_SET + 1, + .get = &ip_set_sockfn_get, + .use = 0 +}; + +MODULE_PARM(max_sets, "i"); +MODULE_PARM_DESC(max_sets, "maximal number of sets"); + +static int __init init(void) +{ + int res; + + if (max_sets <= 0) + max_sets = CONFIG_IP_NF_SET_MAX; + ip_set_list = vmalloc(sizeof(struct ip_set *) * max_sets); + if (!ip_set_list) { + printk(KERN_ERR "Unable to create ip_set_list\n"); + return -ENOMEM; + } + memset(ip_set_list, 0, sizeof(struct ip_set *) * max_sets); + INIT_LIST_HEAD(&set_type_list); + + res = nf_register_sockopt(&so_set); + if (res != 0) { + ip_set_printk("SO_SET registry failed: %d", res); + vfree(ip_set_list); + return res; + } + return 0; +} + +static void __exit fini(void) +{ + nf_unregister_sockopt(&so_set); + vfree(ip_set_list); + DP("these are the famous last words"); +} + +EXPORT_SYMBOL(ip_set_register_set_type); +EXPORT_SYMBOL(ip_set_unregister_set_type); + +EXPORT_SYMBOL(ip_set_list); +EXPORT_SYMBOL(ip_set_get_byname); +EXPORT_SYMBOL(ip_set_get_byid); +EXPORT_SYMBOL(ip_set_put); + +EXPORT_SYMBOL(ip_set_addip_kernel); +EXPORT_SYMBOL(ip_set_delip_kernel); +EXPORT_SYMBOL(ip_set_testip_kernel); + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_set_iphash.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_set_iphash.c --- linux-2.4.26/net/ipv4/netfilter/ip_set_iphash.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_set_iphash.c Sun Apr 18 17:33:10 2004 @@ -0,0 +1,319 @@ +/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +/* Kernel module implementing an ip hash set */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +static inline ip_set_ip_t +hash_ip(const struct ip_set_iphash *map, ip_set_ip_t ip) +{ + return (jhash_1word(ip & map->netmask, map->initval) % map->hashsize); +} + +static inline int +__testip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + *id = hash_ip(map, ip); + return (map->members[*id] == ip); +} + +static int +matchip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + return __testip(private, ip, id); +} + +static int +testip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_iphash *req = + (struct ip_set_req_iphash *) data; + + if (size != sizeof(struct ip_set_req_iphash)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_iphash), + size); + return -EINVAL; + } + return __testip(private, req->ip, id); +} + +static int +testip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __testip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static inline int +__addip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id, + u_int32_t flags) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + *id = hash_ip(map, ip); + + if (map->members[*id] == ip) + return -EEXIST; + + if (map->members[*id] != 0 && !(flags & IPSET_ADD_OVERWRITE)) + return -EADDRINUSE; + + map->members[*id] = ip; + return 0; +} + +static int +addip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_iphash *req = + (struct ip_set_req_iphash *) data; + + if (size != sizeof(struct ip_set_req_iphash)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_iphash), + size); + return -EINVAL; + } + return __addip(private, req->ip, id, req->flags); +} + +static int +addip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __addip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id, + flags); +} + +static inline int +__delip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + *id = hash_ip(map, ip); + + if (map->members[*id] == 0) + return -EEXIST; + + if (map->members[*id] != ip) + return -EADDRINUSE; + + map->members[*id] = 0; + return 0; +} + +static int +delip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_iphash *req = + (struct ip_set_req_iphash *) data; + + if (size != sizeof(struct ip_set_req_iphash)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_iphash), + size); + return -EINVAL; + } + return __delip(private, req->ip, id); +} + +static int +delip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __delip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static int create(struct ip_set_private **private, const void *data, size_t size) +{ + int newbytes; + struct ip_set_req_iphash_create *req = + (struct ip_set_req_iphash_create *) data; + struct ip_set_iphash *map; + + if (size != sizeof(struct ip_set_req_iphash_create)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_iphash_create), + size); + return -EINVAL; + } + + if (req->hashsize > MAX_RANGE) { + ip_set_printk("hashsize too big (max %d)", + MAX_RANGE); + return -ERANGE; + } + + if (req->hashsize < 1) { + ip_set_printk("hashsize too small"); + return -ERANGE; + } + + map = kmalloc(sizeof(struct ip_set_iphash), GFP_KERNEL); + if (!map) { + DP("out of memory for %d bytes", + sizeof(struct ip_set_iphash)); + return -ENOMEM; + } + map->initval = req->initval; + map->hashsize = req->hashsize; + map->netmask = req->netmask; + newbytes = map->hashsize * sizeof(ip_set_ip_t); + map->members = vmalloc(newbytes); + if (!map->members) { + DP("out of memory for %d bytes", newbytes); + kfree(map); + return -ENOMEM; + } + memset(map->members, 0, newbytes); + + *private = (struct ip_set_private *) map; + return 0; +} + +static void destroy(struct ip_set_private **private) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) *private; + + vfree(map->members); + kfree(map); + + *private = NULL; +} + +static void flush(struct ip_set_private *private) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + memset(map->members, 0, map->hashsize * sizeof(ip_set_ip_t)); +} + +static int list_header_size(const struct ip_set_private *private) +{ + return sizeof(struct ip_set_req_iphash_create); +} + +static void list_header(const struct ip_set_private *private, void *data) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + struct ip_set_req_iphash_create *header = + (struct ip_set_req_iphash_create *) data; + + header->initval = map->initval; + header->hashsize = map->hashsize; + header->netmask = map->netmask; +} + +static int list_members_size(const struct ip_set_private *private) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + return (map->hashsize * sizeof(ip_set_ip_t)); +} + +static void list_members(const struct ip_set_private *private, void *data) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + int bytes = map->hashsize * sizeof(ip_set_ip_t); + + memcpy(data, map->members, bytes); +} + +static ip_set_ip_t sizeid(const struct ip_set_private *private) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + return (map->hashsize); +} + +static struct ip_set_type ip_set_iphash = { + .typename = SETTYPE_NAME, + .typecode = IPSET_TYPE_IP, + .protocol_version = IP_SET_PROTOCOL_VERSION, + .create = &create, + .destroy = &destroy, + .flush = &flush, + .reqsize = sizeof(struct ip_set_req_iphash), + .addip = &addip, + .addip_kernel = &addip_kernel, + .delip = &delip, + .delip_kernel = &delip_kernel, + .matchip = &matchip, + .testip = &testip, + .testip_kernel = &testip_kernel, + .list_header_size = &list_header_size, + .list_header = &list_header, + .list_members_size = &list_members_size, + .list_members = &list_members, + .sizeid = &sizeid, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ip_set_register_set_type(&ip_set_iphash); +} + +static void __exit fini(void) +{ + /* FIXME: possible race with ip_set_create() */ + ip_set_unregister_set_type(&ip_set_iphash); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_set_ipmap.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_set_ipmap.c --- linux-2.4.26/net/ipv4/netfilter/ip_set_ipmap.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_set_ipmap.c Sun Apr 18 17:33:10 2004 @@ -0,0 +1,348 @@ +/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu) + * Patrick Schaaf (bof@bof.de) + * Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +/* Kernel module implementing an IP set type: the single bitmap type */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static inline ip_set_ip_t +ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip) +{ + return ((ip & map->netmask) - map->first_ip)/map->hosts; +} + +static inline int +__testip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + + *id = ip_to_id(map, ip); + return !!test_bit(*id, map->members); +} + +static int +matchip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + return __testip(private, ip, id); +} + +static int +testip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_ipmap *req = + (struct ip_set_req_ipmap *) data; + + if (size != sizeof(struct ip_set_req_ipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_ipmap), + size); + return -EINVAL; + } + return __testip(private, req->ip, id); +} + +static int +testip_kernel(struct ip_set_private *private, + const struct sk_buff *skb, + u_int32_t flags, + ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + DP("flags: %u (%s) ip %u.%u.%u.%u", flags, + flags & IPSET_SRC ? "SRC" : "DST", + NIPQUAD(skb->nh.iph->saddr)); + DP("flags: %u (%s) ip %u.%u.%u.%u", flags, + flags & IPSET_SRC ? "SRC" : "DST", + NIPQUAD(skb->nh.iph->daddr)); + + return __testip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static inline int +__addip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + + *id = ip_to_id(map, ip); + if (test_and_set_bit(*id, map->members)) + return -EEXIST; + + return 0; +} + +static int +addip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_ipmap *req = + (struct ip_set_req_ipmap *) data; + + if (size != sizeof(struct ip_set_req_ipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_ipmap), + size); + return -EINVAL; + } + DP("%u.%u.%u.%u", NIPQUAD(req->ip)); + return __addip(private, req->ip, id); +} + +static int +addip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __addip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static inline int +__delip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + + *id = ip_to_id(map, ip); + if (!test_and_clear_bit(*id, map->members)) + return -EEXIST; + + return 0; +} + +static int +delip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_ipmap *req = + (struct ip_set_req_ipmap *) data; + + if (size != sizeof(struct ip_set_req_ipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_ipmap), + size); + return -EINVAL; + } + return __delip(private, req->ip, id); +} + +static int +delip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __delip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static int create(struct ip_set_private **private, const void *data, size_t size) +{ + int newbytes; + struct ip_set_req_ipmap_create *req = + (struct ip_set_req_ipmap_create *) data; + struct ip_set_ipmap *map; + + if (size != sizeof(struct ip_set_req_ipmap_create)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_ipmap_create), + size); + return -EINVAL; + } + + DP("from 0x%08x to 0x%08x", req->from, req->to); + + if (req->from > req->to) { + DP("bad ip range"); + return -EINVAL; + } + + if (req->to - req->from > MAX_RANGE) { + ip_set_printk("range too big (max %d addresses)", + MAX_RANGE); + return -ERANGE; + } + + map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL); + if (!map) { + DP("out of memory for %d bytes", + sizeof(struct ip_set_ipmap)); + return -ENOMEM; + } + map->first_ip = req->from; + map->last_ip = req->to; + map->netmask = req->netmask; + + if (req->netmask == 0xFFFFFFFF) { + map->hosts = 1; + map->sizeid = map->last_ip - map->first_ip + 1; + } else { + unsigned int mask_bits, netmask_bits; + ip_set_ip_t mask; + + map->first_ip &= map->netmask; /* Should we better bark? */ + + mask = range_to_mask(map->first_ip, map->last_ip, &mask_bits); + netmask_bits = mask_to_bits(map->netmask); + + if (!mask || netmask_bits <= mask_bits) + return -EINVAL; + + map->hosts = 2 << (32 - netmask_bits - 1); + map->sizeid = 2 << (netmask_bits - mask_bits - 1); + } + newbytes = bitmap_bytes(0, map->sizeid - 1); + DP("%x %x %i", map->first_ip, map->last_ip, newbytes); + map->members = kmalloc(newbytes, GFP_KERNEL); + if (!map->members) { + DP("out of memory for %d bytes", newbytes); + kfree(map); + return -ENOMEM; + } + memset(map->members, 0, newbytes); + + *private = (struct ip_set_private *) map; + return 0; +} + +static void destroy(struct ip_set_private **private) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) *private; + + kfree(map->members); + kfree(map); + + *private = NULL; +} + +static void flush(struct ip_set_private *private) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1)); +} + +static int list_header_size(const struct ip_set_private *private) +{ + return sizeof(struct ip_set_req_ipmap_create); +} + +static void list_header(const struct ip_set_private *private, void *data) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + struct ip_set_req_ipmap_create *header = + (struct ip_set_req_ipmap_create *) data; + + DP("list_header %x %x", map->first_ip, map->last_ip); + + header->from = map->first_ip; + header->to = map->last_ip; + header->netmask = map->netmask; +} + +static int list_members_size(const struct ip_set_private *private) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + return bitmap_bytes(0, map->sizeid - 1); +} + +static void list_members(const struct ip_set_private *private, void *data) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + int bytes = bitmap_bytes(0, map->sizeid - 1); + + memcpy(data, map->members, bytes); +} + +static ip_set_ip_t sizeid(const struct ip_set_private *private) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + return (map->sizeid); +} + +static struct ip_set_type ip_set_ipmap = { + .typename = SETTYPE_NAME, + .typecode = IPSET_TYPE_IP, + .protocol_version = IP_SET_PROTOCOL_VERSION, + .create = &create, + .destroy = &destroy, + .flush = &flush, + .reqsize = sizeof(struct ip_set_req_ipmap), + .addip = &addip, + .addip_kernel = &addip_kernel, + .delip = &delip, + .delip_kernel = &delip_kernel, + .matchip = &matchip, + .testip = &testip, + .testip_kernel = &testip_kernel, + .list_header_size = &list_header_size, + .list_header = &list_header, + .list_members_size = &list_members_size, + .list_members = &list_members, + .sizeid = &sizeid, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ip_set_register_set_type(&ip_set_ipmap); +} + +static void __exit fini(void) +{ + /* FIXME: possible race with ip_set_create() */ + ip_set_unregister_set_type(&ip_set_ipmap); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_set_macipmap.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_set_macipmap.c --- linux-2.4.26/net/ipv4/netfilter/ip_set_macipmap.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_set_macipmap.c Sun Apr 18 17:33:10 2004 @@ -0,0 +1,371 @@ +/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu) + * Patrick Schaaf (bof@bof.de) + * Martin Josefsson (gandalf@wlug.westbo.se) + * + * 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 + */ + +/* Kernel module implementing an IP set type: the macipmap type */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int +matchip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_macipmap *map = (struct ip_set_macipmap *) private; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + + *id = ip - map->first_ip; + return !!test_bit(IPSET_MACIP_ISSET, &table[*id].flags); +} + +static int +testip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_macipmap *map = (struct ip_set_macipmap *) private; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + + struct ip_set_req_macipmap *req = + (struct ip_set_req_macipmap *) data; + + if (size != sizeof(struct ip_set_req_macipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_macipmap), + size); + return -EINVAL; + } + + if (req->ip < map->first_ip || req->ip > map->last_ip) + return -ERANGE; + + *id = req->ip - map->first_ip; + if (test_bit(IPSET_MACIP_ISSET, &table[*id].flags)) { + /* Is mac pointer valid? + * If so, compare... */ + return (memcmp(req->ethernet, + &table[*id].ethernet, + ETH_ALEN) == 0); + } else { + return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); + } +} + +static int +testip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + ip_set_ip_t ip; + + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr); + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + + *id = ip - map->first_ip; + if (test_bit(IPSET_MACIP_ISSET, &table[*id].flags)) { + /* Is mac pointer valid? + * If so, compare... */ + return (skb->mac.raw >= skb->head + && (skb->mac.raw + ETH_HLEN) <= skb->data + && (memcmp(skb->mac.ethernet->h_source, + &table[*id].ethernet, + ETH_ALEN) == 0)); + } else { + return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); + } +} + +/* returns 0 on success */ +static inline int +__addip(struct ip_set_private *private, + ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *id) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + if (test_and_set_bit(IPSET_MACIP_ISSET, + &table[ip - map->first_ip].flags)) + return -EEXIST; + + *id = ip - map->first_ip; + memcpy(&table[*id].ethernet, ethernet, ETH_ALEN); + return 0; +} + +static int +addip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_macipmap *req = + (struct ip_set_req_macipmap *) data; + + if (size != sizeof(struct ip_set_req_macipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_macipmap), + size); + return -EINVAL; + } + return __addip(private, req->ip, req->ethernet, id); +} + +static int +addip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + ip_set_ip_t ip; + + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr); + + if (!(skb->mac.raw >= skb->head + && (skb->mac.raw + ETH_HLEN) <= skb->data)) + return -EINVAL; + + return __addip(private, ip, skb->mac.ethernet->h_source, id); +} + +static inline int +__delip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + if (!test_and_clear_bit(IPSET_MACIP_ISSET, + &table[ip - map->first_ip].flags)) + return -EEXIST; + + *id = ip - map->first_ip; + return 0; +} + +static int +delip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_macipmap *req = + (struct ip_set_req_macipmap *) data; + + if (size != sizeof(struct ip_set_req_macipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_macipmap), + size); + return -EINVAL; + } + return __delip(private, req->ip, id); +} + +static int +delip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __delip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static int create(struct ip_set_private **private, const void *data, size_t size) +{ + int newbytes; + struct ip_set_req_macipmap_create *req = + (struct ip_set_req_macipmap_create *) data; + struct ip_set_macipmap *map; + + if (size != sizeof(struct ip_set_req_macipmap_create)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_macipmap_create), + size); + return -EINVAL; + } + + DP("from 0x%08x to 0x%08x", req->from, req->to); + + if (req->from > req->to) { + DP("bad ip range"); + return -EINVAL; + } + + if (req->to - req->from > MAX_RANGE) { + ip_set_printk("range too big (max %d addresses)", + MAX_RANGE); + return -ERANGE; + } + + map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL); + if (!map) { + DP("out of memory for %d bytes", + sizeof(struct ip_set_macipmap)); + return -ENOMEM; + } + map->flags = req->flags; + map->first_ip = req->from; + map->last_ip = req->to; + newbytes = (req->to - req->from + 1) * sizeof(struct ip_set_macip); + map->members = vmalloc(newbytes); + if (!map->members) { + DP("out of memory for %d bytes", newbytes); + kfree(map); + return -ENOMEM; + } + memset(map->members, 0, newbytes); + + *private = (struct ip_set_private *) map; + return 0; +} + +static void destroy(struct ip_set_private **private) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) *private; + + vfree(map->members); + kfree(map); + + *private = NULL; +} + +static void flush(struct ip_set_private *private) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + memset(map->members, 0, (map->last_ip - map->first_ip) + * sizeof(struct ip_set_macip)); +} + +static int list_header_size(const struct ip_set_private *private) +{ + return sizeof(struct ip_set_req_macipmap_create); +} + +static void list_header(const struct ip_set_private *private, void *data) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + struct ip_set_req_macipmap_create *header = + (struct ip_set_req_macipmap_create *) data; + + DP("list_header %x %x %u", map->first_ip, map->last_ip, + map->flags); + + header->from = map->first_ip; + header->to = map->last_ip; + header->flags = map->flags; +} + +static int list_members_size(const struct ip_set_private *private) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + + return (map->last_ip + - map->first_ip + 1) * sizeof(struct ip_set_macip); +} + +static void list_members(const struct ip_set_private *private, void *data) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + + int bytes = (map->last_ip - + - map->first_ip + 1) * sizeof(struct ip_set_macip); + + memcpy(data, map->members, bytes); +} + +static ip_set_ip_t sizeid(const struct ip_set_private *private) +{ + struct ip_set_macipmap *map = (struct ip_set_macipmap *) private; + + return (map->last_ip - map->first_ip + 1); +} + +static struct ip_set_type ip_set_macipmap = { + .typename = SETTYPE_NAME, + .typecode = IPSET_TYPE_IP, + .protocol_version = IP_SET_PROTOCOL_VERSION, + .create = &create, + .destroy = &destroy, + .flush = &flush, + .reqsize = sizeof(struct ip_set_req_macipmap), + .addip = &addip, + .addip_kernel = &addip_kernel, + .delip = &delip, + .delip_kernel = &delip_kernel, + .matchip = &matchip, + .testip = &testip, + .testip_kernel = &testip_kernel, + .list_header_size = &list_header_size, + .list_header = &list_header, + .list_members_size = &list_members_size, + .list_members = &list_members, + .sizeid = &sizeid, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ip_set_register_set_type(&ip_set_macipmap); +} + +static void __exit fini(void) +{ + /* FIXME: possible race with ip_set_create() */ + ip_set_unregister_set_type(&ip_set_macipmap); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_set_portmap.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_set_portmap.c --- linux-2.4.26/net/ipv4/netfilter/ip_set_portmap.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_set_portmap.c Sun Apr 18 17:33:10 2004 @@ -0,0 +1,363 @@ +/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * Based on ip_set_ipmap.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +/* Kernel module implementing a port set type as a bitmap */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static inline ip_set_ip_t +get_port(const struct sk_buff *skb, u_int32_t flags) +{ + struct iphdr *iph = skb->nh.iph; + u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; + + switch (iph->protocol) { + case IPPROTO_TCP: { + struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); + + /* See comments at tcp_match in ip_tables.c */ + if (offset != 0 + || (offset == 0 + && (skb->len - iph->ihl * 4) < sizeof(struct tcphdr))) + return INVALID_PORT; + + return ntohs(flags & IPSET_SRC ? + tcph->source : tcph->dest); + } + case IPPROTO_UDP: { + struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl); + + if (offset != 0 + || (offset == 0 + && (skb->len - iph->ihl * 4) < sizeof(struct udphdr))) + return INVALID_PORT; + + return ntohs(flags & IPSET_SRC ? + udph->source : udph->dest); + } + default: + return INVALID_PORT; + } +} + +static inline int +__testport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + if (port < map->first_port || port > map->last_port) + return -ERANGE; + + *id = port - map->first_port; + return !!test_bit(*id, map->members); +} + +static int +matchport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id) +{ + return __testport(private, port, id); +} + +static int +testport(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_portmap *req = + (struct ip_set_req_portmap *) data; + + if (size != sizeof(struct ip_set_req_portmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_portmap), + size); + return -EINVAL; + } + return __testport(private, req->port, id); +} + +static int +testport_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + ip_set_ip_t port; + + if (!(flags & IPSET_TYPE_PORT)) + return -EINVAL; + + port = get_port(skb, flags); + DP("flags %u %s port %u", + flags, + flags & IPSET_SRC ? "SRC" : "DST", + port); + + if (port == INVALID_PORT) + return -EINVAL; + + return __testport(private, port, id); +} + +static inline int +__addport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + if (port < map->first_port || port > map->last_port) + return -ERANGE; + if (test_and_set_bit(port - map->first_port, map->members)) + return -EEXIST; + + *id = port - map->first_port; + return 0; +} + +static int +addport(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_portmap *req = + (struct ip_set_req_portmap *) data; + + if (size != sizeof(struct ip_set_req_portmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_portmap), + size); + return -EINVAL; + } + return __addport(private, req->port, id); +} + +static int +addport_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + ip_set_ip_t port; + + if (!(flags & IPSET_TYPE_PORT)) + return -EINVAL; + + port = get_port(skb, flags); + + if (port == INVALID_PORT) + return -EINVAL; + + return __addport(private, port, id); +} + +static inline int +__delport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + if (port < map->first_port || port > map->last_port) + return -ERANGE; + if (!test_and_clear_bit(port - map->first_port, map->members)) + return -EEXIST; + + *id = port - map->first_port; + return 0; +} + +static int +delport(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_portmap *req = + (struct ip_set_req_portmap *) data; + + if (size != sizeof(struct ip_set_req_portmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_portmap), + size); + return -EINVAL; + } + return __delport(private, req->port, id); +} + +static int +delport_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + ip_set_ip_t port; + + if (!(flags & IPSET_TYPE_PORT)) + return -EINVAL; + + port = get_port(skb, flags); + + if (port == INVALID_PORT) + return -EINVAL; + + return __delport(private, port, id); +} + +static int create(struct ip_set_private **private, const void *data, size_t size) +{ + int newbytes; + struct ip_set_req_portmap_create *req = + (struct ip_set_req_portmap_create *) data; + struct ip_set_portmap *map; + + if (size != sizeof(struct ip_set_req_portmap_create)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_portmap_create), + size); + return -EINVAL; + } + + DP("from 0x%08x to 0x%08x", req->from, req->to); + + if (req->from > req->to) { + DP("bad port range"); + return -EINVAL; + } + + if (req->to - req->from > MAX_RANGE) { + ip_set_printk("range too big (max %d ports)", + MAX_RANGE); + return -ERANGE; + } + + map = kmalloc(sizeof(struct ip_set_portmap), GFP_KERNEL); + if (!map) { + DP("out of memory for %d bytes", + sizeof(struct ip_set_portmap)); + return -ENOMEM; + } + map->first_port = req->from; + map->last_port = req->to; + newbytes = bitmap_bytes(req->from, req->to); + map->members = kmalloc(newbytes, GFP_KERNEL); + if (!map->members) { + DP("out of memory for %d bytes", newbytes); + kfree(map); + return -ENOMEM; + } + memset(map->members, 0, newbytes); + + *private = (struct ip_set_private *) map; + return 0; +} + +static void destroy(struct ip_set_private **private) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) *private; + + kfree(map->members); + kfree(map); + + *private = NULL; +} + +static void flush(struct ip_set_private *private) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + memset(map->members, 0, bitmap_bytes(map->first_port, map->last_port)); +} + +static int list_header_size(const struct ip_set_private *private) +{ + return sizeof(struct ip_set_req_portmap_create); +} + +static void list_header(const struct ip_set_private *private, void *data) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + struct ip_set_req_portmap_create *header = + (struct ip_set_req_portmap_create *) data; + + DP("list_header %x %x", map->first_port, map->last_port); + + header->from = map->first_port; + header->to = map->last_port; +} + +static int list_members_size(const struct ip_set_private *private) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + return bitmap_bytes(map->first_port, map->last_port); +} + +static void list_members(const struct ip_set_private *private, void *data) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + int bytes = bitmap_bytes(map->first_port, map->last_port); + + memcpy(data, map->members, bytes); +} + +static ip_set_ip_t sizeid(const struct ip_set_private *private) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + return (map->last_port - map->first_port + 1); +} + +static struct ip_set_type ip_set_portmap = { + .typename = SETTYPE_NAME, + .typecode = IPSET_TYPE_PORT, + .protocol_version = IP_SET_PROTOCOL_VERSION, + .create = &create, + .destroy = &destroy, + .flush = &flush, + .reqsize = sizeof(struct ip_set_req_portmap), + .addip = &addport, + .addip_kernel = &addport_kernel, + .delip = &delport, + .delip_kernel = &delport_kernel, + .matchip = &matchport, + .testip = &testport, + .testip_kernel = &testport_kernel, + .list_header_size = &list_header_size, + .list_header = &list_header, + .list_members_size = &list_members_size, + .list_members = &list_members, + .sizeid = &sizeid, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ip_set_register_set_type(&ip_set_portmap); +} + +static void __exit fini(void) +{ + /* FIXME: possible race with ip_set_create() */ + ip_set_unregister_set_type(&ip_set_portmap); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.26/net/ipv4/netfilter/ip_tables.c linux-2.4.26-pomng/net/ipv4/netfilter/ip_tables.c --- linux-2.4.26/net/ipv4/netfilter/ip_tables.c Thu Feb 19 11:20:08 2004 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ip_tables.c Sun Apr 18 17:33:08 2004 @@ -1699,9 +1699,9 @@ = { { NULL, NULL }, "icmp", &icmp_match, &icmp_checkentry, NULL }; #ifdef CONFIG_PROC_FS -static inline int print_name(const char *i, - off_t start_offset, char *buffer, int length, - off_t *pos, unsigned int *count) +static int print_name(const char *i, + off_t start_offset, char *buffer, int length, + off_t *pos, unsigned int *count) { if ((*count)++ >= start_offset) { unsigned int namelen; @@ -1735,6 +1735,15 @@ return pos; } +static inline int print_target(const struct ipt_target *t, + off_t start_offset, char *buffer, int length, + off_t *pos, unsigned int *count) +{ + if (t != &ipt_standard_target && t != &ipt_error_target) + return 0; + return print_name((char *)t, start_offset, buffer, length, pos, count); +} + static int ipt_get_targets(char *buffer, char **start, off_t offset, int length) { off_t pos = 0; @@ -1743,7 +1752,7 @@ if (down_interruptible(&ipt_mutex) != 0) return 0; - LIST_FIND(&ipt_target, print_name, void *, + LIST_FIND(&ipt_target, print_target, struct ipt_target *, offset, buffer, length, &pos, &count); up(&ipt_mutex); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_CLASSIFY.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_CLASSIFY.c --- linux-2.4.26/net/ipv4/netfilter/ipt_CLASSIFY.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_CLASSIFY.c Sun Apr 18 17:33:01 2004 @@ -0,0 +1,82 @@ +/* + * This is a module which is used for setting the skb->priority field + * of an skb for qdisc classification. + */ + +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Patrick McHardy "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("iptables qdisc classification target module"); + +static unsigned int +target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ipt_classify_target_info *clinfo = targinfo; + + if((*pskb)->priority != clinfo->priority) { + (*pskb)->priority = clinfo->priority; + (*pskb)->nfcache |= NFC_ALTERED; + } + + return IPT_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_classify_target_info))){ + printk(KERN_ERR "CLASSIFY: invalid size (%u != %u).\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_classify_target_info))); + return 0; + } + + if (hook_mask & ~(1 << NF_IP_POST_ROUTING)) { + printk(KERN_ERR "CLASSIFY: only valid in POST_ROUTING.\n"); + return 0; + } + + if (strcmp(tablename, "mangle") != 0) { + printk(KERN_WARNING "CLASSIFY: can only be called from " + "\"mangle\" table, not \"%s\".\n", + tablename); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_classify_reg += { { NULL, NULL }, "CLASSIFY", target, checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_target(&ipt_classify_reg)) + return -EINVAL; + + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_classify_reg); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c --- linux-2.4.26/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c Sun Apr 18 17:33:02 2004 @@ -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("Strip all options in IPv4 packets"); +MODULE_LICENSE("GPL"); + +static unsigned int +target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct sk_buff *skb = (*pskb); + struct ip_options * opt; + unsigned char * optiph = skb->nh.raw; + int l = ((struct ip_options *)(&(IPCB(skb)->opt)))->optlen; + + + /* if no options in packet then nothing to clear. */ + if (iph->ihl * 4 == sizeof(struct iphdr)) + return IPT_CONTINUE; + + /* else clear all options */ + memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); + memset(optiph+sizeof(struct iphdr), IPOPT_NOOP, l); + opt = &(IPCB(skb)->opt); + opt->is_data = 0; + opt->optlen = l; + + skb->nfcache |= NFC_ALTERED; + + return IPT_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (strcmp(tablename, "mangle")) { + printk(KERN_WARNING "IPV4OPTSSTRIP: can only be called from \"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + /* nothing else to check because no parameters */ + return 1; +} + +static struct ipt_target ipt_ipv4optsstrip_reg += { { NULL, NULL }, "IPV4OPTSSTRIP", target, checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_target(&ipt_ipv4optsstrip_reg)) + return -EINVAL; + printk("ipt_IPV4OPTSSTRIP loaded\n"); + + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_ipv4optsstrip_reg); + printk("ipt_IPV4OPTSSTRIP unloaded\n"); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_LOG.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_LOG.c --- linux-2.4.26/net/ipv4/netfilter/ipt_LOG.c Tue Oct 7 13:31:46 2003 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_LOG.c Sun Apr 18 17:33:07 2004 @@ -12,6 +12,11 @@ #include #include +#include + +static unsigned int nflog = 1; +MODULE_PARM(nflog, "i"); +MODULE_PARM_DESC(nflog, "register as internal netfilter logging module"); #if 0 #define DEBUGP printk @@ -297,23 +302,21 @@ /* maxlen = 230+ 91 + 230 + 252 = 803 */ } -static unsigned int -ipt_log_target(struct sk_buff **pskb, +static void +ipt_log_packet(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, const struct net_device *out, - const void *targinfo, - void *userinfo) + const struct ipt_log_info *loginfo, + const char *level_string, + const char *prefix) { struct iphdr *iph = (*pskb)->nh.iph; - const struct ipt_log_info *loginfo = targinfo; - char level_string[4] = "< >"; - level_string[1] = '0' + (loginfo->level % 8); spin_lock_bh(&log_lock); printk(level_string); printk("%sIN=%s OUT=%s ", - loginfo->prefix, + prefix == NULL ? loginfo->prefix : prefix, in ? in->name : "", out ? out->name : ""); if (in && !out) { @@ -333,10 +336,59 @@ dump_packet(loginfo, iph, (*pskb)->len, 1); printk("\n"); spin_unlock_bh(&log_lock); +} + +static unsigned int +ipt_log_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ipt_log_info *loginfo = targinfo; + char level_string[4] = "< >"; + + level_string[1] = '0' + (loginfo->level % 8); + ipt_log_packet(pskb, hooknum, in, out, loginfo, level_string, NULL); return IPT_CONTINUE; } +static void +ip_log_packet_fn(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ + struct ipt_log_info loginfo = { + .level = 0, + .logflags = IPT_LOG_MASK, + .prefix = "" + }; + + ipt_log_packet(pskb, hooknum, in, out, &loginfo, KERN_WARNING, prefix); +} + +static void +ip_log_fn(char *pfh, size_t len, + const char *prefix) +{ + struct iphdr *iph = (struct iphdr *)pfh; + struct ipt_log_info loginfo = { + .level = 0, + .logflags = IPT_LOG_MASK, + .prefix = "", + }; + + spin_lock_bh(&log_lock); + printk(KERN_WARNING "%s", prefix); + dump_packet(&loginfo, iph, len, 1); + printk("\n"); + spin_unlock_bh(&log_lock); +} + static int ipt_log_checkentry(const char *tablename, const struct ipt_entry *e, void *targinfo, @@ -368,17 +420,23 @@ static struct ipt_target ipt_log_reg = { { NULL, NULL }, "LOG", ipt_log_target, ipt_log_checkentry, NULL, THIS_MODULE }; +static struct nf_logging_t ip_logging_fn += { ip_log_packet_fn, ip_log_fn }; static int __init init(void) { if (ipt_register_target(&ipt_log_reg)) return -EINVAL; - + if (nflog) + nf_ip_log_register(&ip_logging_fn); + return 0; } static void __exit fini(void) { + if (nflog) + nf_ip_log_unregister(&ip_logging_fn); ipt_unregister_target(&ipt_log_reg); } diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_NETLINK.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_NETLINK.c --- linux-2.4.26/net/ipv4/netfilter/ipt_NETLINK.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_NETLINK.c Sun Apr 18 17:33:02 2004 @@ -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_ATOMIC); + }else{ + if (net_ratelimit()) + printk(KERN_WARNING "ipt_NETLINK: packet drop due to netlink failure\n"); + } + + if ( MASK(nld->flags, USE_DROP) ) + return NF_DROP; + + return IPT_CONTINUE; +} + +static int ipt_netlink_checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hookmask) +{ + //struct ipt_nldata *nld = (struct ipt_nldata *)targinfo; + + return 1; +} + +static struct ipt_target ipt_netlink_reg = { + {NULL, NULL}, + "NETLINK", + ipt_netlink_target, + ipt_netlink_checkentry, + NULL, + THIS_MODULE +}; + +static int __init init(void) +{ + DEBUGP("ipt_NETLINK: init module\n"); + + if (ipt_register_target(&ipt_netlink_reg) != 0) { + return -EINVAL; + } + + if ( !(ipfwsk=netlink_kernel_create(NETLINK_FIREWALL, NULL)) ){ + return -EINVAL; + } + + return 0; +} + +static void __exit fini(void) +{ + DEBUGP("ipt_NETLINK: cleanup_module\n"); + ipt_unregister_target(&ipt_netlink_reg); + if(ipfwsk->socket) sock_release(ipfwsk->socket); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_NETMAP.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_NETMAP.c --- linux-2.4.26/net/ipv4/netfilter/ipt_NETMAP.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_NETMAP.c Sun Apr 18 17:33:02 2004 @@ -0,0 +1,112 @@ +/* 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"); +MODULE_AUTHOR("Svenning Soerensen "); +MODULE_DESCRIPTION("iptables 1:1 NAT mapping of IP networks target"); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static int +check(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + const struct ip_nat_multi_range *mr = targinfo; + + if (strcmp(tablename, "nat") != 0) { + DEBUGP(MODULENAME":check: bad table `%s'.\n", tablename); + return 0; + } + if (targinfosize != IPT_ALIGN(sizeof(*mr))) { + DEBUGP(MODULENAME":check: size %u.\n", targinfosize); + return 0; + } + if (hook_mask & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_POST_ROUTING))) { + DEBUGP(MODULENAME":check: bad hooks %x.\n", hook_mask); + return 0; + } + if (!(mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)) { + DEBUGP(MODULENAME":check: bad MAP_IPS.\n"); + return 0; + } + if (mr->rangesize != 1) { + DEBUGP(MODULENAME":check: bad rangesize %u.\n", mr->rangesize); + return 0; + } + return 1; +} + +static unsigned int +target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + u_int32_t new_ip, netmask; + const struct ip_nat_multi_range *mr = targinfo; + struct ip_nat_multi_range newrange; + + IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING + || hooknum == NF_IP_POST_ROUTING); + ct = ip_conntrack_get(*pskb, &ctinfo); + + netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); + + if (hooknum == NF_IP_PRE_ROUTING) + new_ip = (*pskb)->nh.iph->daddr & ~netmask; + else + new_ip = (*pskb)->nh.iph->saddr & ~netmask; + new_ip |= mr->range[0].min_ip & netmask; + + newrange = ((struct ip_nat_multi_range) + { 1, { { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, + new_ip, new_ip, + mr->range[0].min, mr->range[0].max } } }); + + /* Hand modified range to generic setup. */ + return ip_nat_setup_info(ct, &newrange, hooknum); +} + +static struct ipt_target target_module = { + .name = MODULENAME, + .target = target, + .checkentry = check, + .me = 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 -urN linux-2.4.26/net/ipv4/netfilter/ipt_POOL.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_POOL.c --- linux-2.4.26/net/ipv4/netfilter/ipt_POOL.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_POOL.c Sun Apr 18 17:33:08 2004 @@ -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 -urN linux-2.4.26/net/ipv4/netfilter/ipt_ROUTE.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_ROUTE.c --- linux-2.4.26/net/ipv4/netfilter/ipt_ROUTE.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_ROUTE.c Sun Apr 18 17:33:03 2004 @@ -0,0 +1,369 @@ +/* + * This implements the ROUTE target, which enables you to setup unusual + * routes not supported by the standard kernel routing table. + * + * Copyright (C) 2002 Cedric de Launois + * + * v 1.8 2003/07/25 + * + * This software is distributed under GNU GPL v2, 1991 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + + +/* Try to route the packet according to the routing keys specified in + * route_info. Keys are : + * - ifindex : + * 0 if no oif preferred, + * otherwise set to the index of the desired oif + * - route_info->gw : + * 0 if no gateway specified, + * otherwise set to the next host to which the pkt must be routed + * If success, skb->dev is the output device to which the packet must + * be sent and skb->dst is not NULL + * + * RETURN: -1 if an error occured + * 1 if the packet was succesfully routed to the + * destination desired + * 0 if the kernel routing table could not route the packet + * according to the keys specified + */ +static int route(struct sk_buff *skb, + unsigned int ifindex, + const struct ipt_route_target_info *route_info) +{ + int err; + struct rtable *rt; + struct iphdr *iph = skb->nh.iph; + struct rt_key key = { + dst:iph->daddr, + src:0, + oif:ifindex, + tos:RT_TOS(iph->tos) + }; + + /* The destination address may be overloaded by the target */ + if (route_info->gw) + key.dst = route_info->gw; + + /* Trying to route the packet using the standard routing table. */ + if ((err = ip_route_output_key(&rt, &key))) { + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: couldn't route pkt (err: %i)",err); + return -1; + } + + /* Drop old route. */ + dst_release(skb->dst); + skb->dst = NULL; + + /* Success if no oif specified or if the oif correspond to the + * one desired */ + if (!ifindex || rt->u.dst.dev->ifindex == ifindex) { + skb->dst = &rt->u.dst; + skb->dev = skb->dst->dev; + return 1; + } + + /* The interface selected by the routing table is not the one + * specified by the user. This may happen because the dst address + * is one of our own addresses. + */ + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: failed to route as desired gw=%u.%u.%u.%u oif=%i (got oif=%i)\n", + NIPQUAD(route_info->gw), ifindex, rt->u.dst.dev->ifindex); + + return 0; +} + + +/* Stolen from ip_finish_output2 + * PRE : skb->dev is set to the device we are leaving by + * skb->dst is not NULL + * POST: the packet is sent with the link layer header pushed + * the packet is destroyed + */ +static void ip_direct_send(struct sk_buff *skb) +{ + struct dst_entry *dst = skb->dst; + struct hh_cache *hh = dst->hh; + + if (hh) { + read_lock_bh(&hh->hh_lock); + memcpy(skb->data - 16, hh->hh_data, 16); + read_unlock_bh(&hh->hh_lock); + skb_push(skb, hh->hh_len); + hh->hh_output(skb); + } else if (dst->neighbour) + dst->neighbour->output(skb); + else { + if (net_ratelimit()) + DEBUGP(KERN_DEBUG "ipt_ROUTE: no hdr & no neighbour cache!\n"); + kfree_skb(skb); + } +} + + +/* PRE : skb->dev is set to the device we are leaving by + * POST: - the packet is directly sent to the skb->dev device, without + * pushing the link layer header. + * - the packet is destroyed + */ +static inline int dev_direct_send(struct sk_buff *skb) +{ + return dev_queue_xmit(skb); +} + + +static unsigned int route_oif(const struct ipt_route_target_info *route_info, + struct sk_buff *skb) +{ + unsigned int ifindex = 0; + struct net_device *dev_out = NULL; + + /* The user set the interface name to use. + * Getting the current interface index. + */ + if ((dev_out = dev_get_by_name(route_info->oif))) { + ifindex = dev_out->ifindex; + } else { + /* Unknown interface name : packet dropped */ + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: oif interface %s not found\n", route_info->oif); + return NF_DROP; + } + + /* Trying the standard way of routing packets */ + switch (route(skb, ifindex, route_info)) { + case 1: + dev_put(dev_out); + if (route_info->flags & IPT_ROUTE_CONTINUE) + return IPT_CONTINUE; + + ip_direct_send(skb); + return NF_STOLEN; + + case 0: + /* Failed to send to oif. Trying the hard way */ + if (route_info->flags & IPT_ROUTE_CONTINUE) + return NF_DROP; + + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: forcing the use of %i\n", + ifindex); + + /* We have to force the use of an interface. + * This interface must be a tunnel interface since + * otherwise we can't guess the hw address for + * the packet. For a tunnel interface, no hw address + * is needed. + */ + if ((dev_out->type != ARPHRD_TUNNEL) + && (dev_out->type != ARPHRD_IPGRE)) { + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: can't guess the hw addr !\n"); + dev_put(dev_out); + return NF_DROP; + } + + /* Send the packet. This will also free skb + * Do not go through the POST_ROUTING hook because + * skb->dst is not set and because it will probably + * get confused by the destination IP address. + */ + skb->dev = dev_out; + dev_direct_send(skb); + dev_put(dev_out); + return NF_STOLEN; + + default: + /* Unexpected error */ + dev_put(dev_out); + return NF_DROP; + } +} + + +static unsigned int route_iif(const struct ipt_route_target_info *route_info, + struct sk_buff *skb) +{ + struct net_device *dev_out = NULL; + unsigned int ifindex = 0; + + /* Getting the current interface index. */ + if ((dev_out = dev_get_by_name(route_info->iif))) + ifindex = dev_out->ifindex; + else { + /* Unknown interface name : packet dropped */ + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: iif interface %s not found\n", route_info->oif); + return NF_DROP; + } + + skb->dev = dev_out; + dst_release(skb->dst); + skb->dst = NULL; + + netif_rx(skb); + + return NF_STOLEN; +} + + +static unsigned int route_gw(const struct ipt_route_target_info *route_info, + struct sk_buff *skb) +{ + if (route(skb, 0, route_info)!=1) + return NF_DROP; + + if (route_info->flags & IPT_ROUTE_CONTINUE) + return IPT_CONTINUE; + + ip_direct_send(skb); + return NF_STOLEN; +} + + +static unsigned int ipt_route_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ipt_route_target_info *route_info = targinfo; + struct sk_buff *skb = *pskb; + + /* If we are at PREROUTING or INPUT hook + * the TTL isn't decreased by the IP stack + */ + if (hooknum == NF_IP_PRE_ROUTING || + hooknum == NF_IP_LOCAL_IN) { + + struct iphdr *iph = skb->nh.iph; + + if (iph->ttl <= 1) { + struct rtable *rt; + + if (ip_route_output(&rt, iph->saddr, iph->daddr, + RT_TOS(iph->tos) | RTO_CONN, + 0)) { + return NF_DROP; + } + + if (skb->dev == rt->u.dst.dev) { + /* Drop old route. */ + dst_release(skb->dst); + skb->dst = &rt->u.dst; + + /* this will traverse normal stack, and + * thus call conntrack on the icmp packet */ + icmp_send(skb, ICMP_TIME_EXCEEDED, + ICMP_EXC_TTL, 0); + } + + return NF_DROP; + } + + ip_decrease_ttl(iph); + } + + /* Tell conntrack to forget this packet since it may get confused + * when a packet is leaving with dst address == our address. + * Good idea ? Dunno. Need advice. + */ + if (!(route_info->flags & IPT_ROUTE_CONTINUE)) { + nf_conntrack_put(skb->nfct); + skb->nfct = NULL; + skb->nfcache = 0; +#ifdef CONFIG_NETFILTER_DEBUG + skb->nf_debug = 0; +#endif + } + + if (route_info->oif[0]) + return route_oif(route_info, *pskb); + + if (route_info->iif[0]) + return route_iif(route_info, *pskb); + + if (route_info->gw) + return route_gw(route_info, *pskb); + + if (net_ratelimit()) + DEBUGP(KERN_DEBUG "ipt_ROUTE: no parameter !\n"); + + return IPT_CONTINUE; +} + + +static int ipt_route_checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (strcmp(tablename, "mangle") != 0) { + printk("ipt_ROUTE: bad table `%s', use the `mangle' table.\n", + tablename); + return 0; + } + + if (hook_mask & ~( (1 << NF_IP_PRE_ROUTING) + | (1 << NF_IP_LOCAL_IN) + | (1 << NF_IP_FORWARD) + | (1 << NF_IP_LOCAL_OUT) + | (1 << NF_IP_POST_ROUTING))) { + printk("ipt_ROUTE: bad hook\n"); + return 0; + } + + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_route_target_info))) { + printk(KERN_WARNING "ipt_ROUTE: targinfosize %u != %Zu\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_route_target_info))); + return 0; + } + + return 1; +} + + +static struct ipt_target ipt_route_reg += { { NULL, NULL }, "ROUTE", ipt_route_target, ipt_route_checkentry, NULL, + THIS_MODULE }; + + +static int __init init(void) +{ + if (ipt_register_target(&ipt_route_reg)) + return -EINVAL; + + return 0; +} + + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_route_reg); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_SAME.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_SAME.c --- linux-2.4.26/net/ipv4/netfilter/ipt_SAME.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_SAME.c Sun Apr 18 17:33:04 2004 @@ -0,0 +1,202 @@ +/* Same. Just like SNAT, only try to make the connections + * between client A and server B always have the same source ip. + * + * (C) 2000 Rusty Russell. GPL. + * + * 010320 Martin Josefsson + * * 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"); +MODULE_AUTHOR("Martin Josefsson "); +MODULE_DESCRIPTION("iptables special SNAT module for consistent sourceip"); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static int +same_check(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + unsigned int count, countess, rangeip, index = 0; + struct ipt_same_info *mr = targinfo; + + mr->ipnum = 0; + + if (strcmp(tablename, "nat") != 0) { + DEBUGP("same_check: bad table `%s'.\n", tablename); + return 0; + } + if (targinfosize != IPT_ALIGN(sizeof(*mr))) { + DEBUGP("same_check: size %u.\n", targinfosize); + return 0; + } + if (hook_mask & ~(1 << NF_IP_PRE_ROUTING | 1 << NF_IP_POST_ROUTING)) { + DEBUGP("same_check: bad hooks %x.\n", hook_mask); + return 0; + } + if (mr->rangesize < 1) { + DEBUGP("same_check: need at least one dest range.\n"); + return 0; + } + if (mr->rangesize > IPT_SAME_MAX_RANGE) { + DEBUGP("same_check: too many ranges specified, maximum " + "is %u ranges\n", + IPT_SAME_MAX_RANGE); + return 0; + } + for (count = 0; count < mr->rangesize; count++) { + if (ntohl(mr->range[count].min_ip) > + ntohl(mr->range[count].max_ip)) { + DEBUGP("same_check: min_ip is larger than max_ip in " + "range `%u.%u.%u.%u-%u.%u.%u.%u'.\n", + NIPQUAD(mr->range[count].min_ip), + NIPQUAD(mr->range[count].max_ip)); + return 0; + } + if (!(mr->range[count].flags & IP_NAT_RANGE_MAP_IPS)) { + DEBUGP("same_check: bad MAP_IPS.\n"); + return 0; + } + rangeip = (ntohl(mr->range[count].max_ip) - + ntohl(mr->range[count].min_ip) + 1); + mr->ipnum += rangeip; + + DEBUGP("same_check: range %u, ipnum = %u\n", count, rangeip); + } + DEBUGP("same_check: total ipaddresses = %u\n", mr->ipnum); + + mr->iparray = kmalloc((sizeof(u_int32_t) * mr->ipnum), GFP_KERNEL); + if (!mr->iparray) { + DEBUGP("same_check: Couldn't allocate %u bytes " + "for %u ipaddresses!\n", + (sizeof(u_int32_t) * mr->ipnum), mr->ipnum); + return 0; + } + DEBUGP("same_check: Allocated %u bytes for %u ipaddresses.\n", + (sizeof(u_int32_t) * mr->ipnum), mr->ipnum); + + for (count = 0; count < mr->rangesize; count++) { + for (countess = ntohl(mr->range[count].min_ip); + countess <= ntohl(mr->range[count].max_ip); + countess++) { + mr->iparray[index] = countess; + DEBUGP("same_check: Added ipaddress `%u.%u.%u.%u' " + "in index %u.\n", + HIPQUAD(countess), index); + index++; + } + } + return 1; +} + +static void +same_destroy(void *targinfo, + unsigned int targinfosize) +{ + struct ipt_same_info *mr = targinfo; + + kfree(mr->iparray); + + DEBUGP("same_destroy: Deallocated %u bytes for %u ipaddresses.\n", + (sizeof(u_int32_t) * mr->ipnum), mr->ipnum); +} + +static unsigned int +same_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + u_int32_t tmpip, aindex, new_ip; + const struct ipt_same_info *mr = targinfo; + struct ip_nat_multi_range newrange; + const struct ip_conntrack_tuple *t; + + IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING || + hooknum == NF_IP_POST_ROUTING); + ct = ip_conntrack_get(*pskb, &ctinfo); + + t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + + /* Base new source on real src ip and optionally dst ip, + giving some hope for consistency across reboots. + Here we calculate the index in mr->iparray which + holds the ipaddress we should use */ + + tmpip = ntohl(t->src.ip); + + if (!(mr->info & IPT_SAME_NODST)) + tmpip += ntohl(t->dst.ip); + + aindex = tmpip % mr->ipnum; + + new_ip = htonl(mr->iparray[aindex]); + + DEBUGP("ipt_SAME: src=%u.%u.%u.%u dst=%u.%u.%u.%u, " + "new src=%u.%u.%u.%u\n", + NIPQUAD(t->src.ip), NIPQUAD(t->dst.ip), + NIPQUAD(new_ip)); + + /* Transfer from original range. */ + newrange = ((struct ip_nat_multi_range) + { 1, { { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, + new_ip, new_ip, + mr->range[0].min, mr->range[0].max } } }); + + /* Hand modified range to generic setup. */ + return ip_nat_setup_info(ct, &newrange, hooknum); +} + +static struct ipt_target same_reg += { { NULL, NULL }, "SAME", same_target, same_check, same_destroy, + THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_target(&same_reg); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&same_reg); +} + +module_init(init); +module_exit(fini); + diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_SET.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_SET.c --- linux-2.4.26/net/ipv4/netfilter/ipt_SET.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_SET.c Sun Apr 18 17:33:10 2004 @@ -0,0 +1,115 @@ +/* ipt_SET.c - netfilter target to manipulate IP sets + * + * This target can be used almost everywhere. It acts on some specified + * IP set, adding or deleting some IP addresses/ports in the set. + * The addresses/ports can be either the source, or destination + * of the packet under inspection. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int +target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ipt_set_info_target *info = targinfo; + + if (info->add_set.id >= 0) + ip_set_addip_kernel(ip_set_list[info->add_set.id], + *pskb, + info->add_set.flags, + info->add_set.set_level, + info->add_set.ip_level); + if (info->del_set.id >= 0) + ip_set_delip_kernel(ip_set_list[info->del_set.id], + *pskb, + info->del_set.flags, + info->del_set.set_level, + info->del_set.ip_level); + + return IPT_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, unsigned int hook_mask) +{ + struct ipt_set_info_target *info = targinfo; + + if (targinfosize != IPT_ALIGN(sizeof(*info))) { + DP("bad target info size %u", targinfosize); + return 0; + } + + if (info->add_set.id >= 0 + && !ip_set_get_byid(info->add_set.id)) { + ip_set_printk("cannot verify add_set id %i as target", + info->add_set.id); + return 0; /* error */ + } + if (info->del_set.id >= 0 + && !ip_set_get_byid(info->del_set.id)) { + ip_set_printk("cannot verify del_set id %i as target", + info->del_set.id); + return 0; /* error */ + } + DP("checkentry OK"); + + return 1; +} + +static void destroy(void *targetinfo, unsigned int targetsize) +{ + struct ipt_set_info_target *info = targetinfo; + + if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) { + ip_set_printk("invalid targetsize %d", targetsize); + return; + } + + if (info->add_set.id >= 0) + ip_set_put(ip_set_list[info->add_set.id]); + if (info->del_set.id >= 0) + ip_set_put(ip_set_list[info->del_set.id]); +} + +static struct ipt_target SET_target = { + .name = "SET", + .target = target, + .checkentry = checkentry, + .destroy = destroy, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_target(&SET_target); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&SET_target); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_TARPIT.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_TARPIT.c --- linux-2.4.26/net/ipv4/netfilter/ipt_TARPIT.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_TARPIT.c Sun Apr 18 17:33:04 2004 @@ -0,0 +1,284 @@ +/* + * Kernel module to capture and hold incoming TCP connections using + * no local per-connection resources. + * + * Based on ipt_REJECT.c and offering functionality similar to + * LaBrea . + * + * Copyright (c) 2002 Aaron Hopkins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Goal: + * - Allow incoming TCP connections to be established. + * - Passing data should result in the connection being switched to the + * persist state (0 byte window), in which the remote side stops sending + * data and asks to continue every 60 seconds. + * - Attempts to shut down the connection should be ignored completely, so + * the remote side ends up having to time it out. + * + * This means: + * - Reply to TCP SYN,!ACK,!RST,!FIN with SYN-ACK, window 5 bytes + * - Reply to TCP SYN,ACK,!RST,!FIN with RST to prevent spoofing + * - Reply to TCP !SYN,!RST,!FIN with ACK, window 0 bytes, rate-limited + */ + +#include +#include +#include +#include +#include +#include +#include +struct in_device; +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + + +/* Stolen from ip_finish_output2 */ +static int ip_direct_send(struct sk_buff *skb) +{ + struct dst_entry *dst = skb->dst; + struct hh_cache *hh = dst->hh; + + if (hh) { + read_lock_bh(&hh->hh_lock); + memcpy(skb->data - 16, hh->hh_data, 16); + read_unlock_bh(&hh->hh_lock); + skb_push(skb, hh->hh_len); + return hh->hh_output(skb); + } else if (dst->neighbour) + return dst->neighbour->output(skb); + + if (net_ratelimit()) + printk(KERN_DEBUG "TARPIT ip_direct_send: no header cache and no neighbor!\n"); + kfree_skb(skb); + return -EINVAL; +} + + +/* Send reply */ +static void tarpit_tcp(struct sk_buff *oskb,struct rtable *ort,int local) +{ + struct sk_buff *nskb; + struct rtable *nrt; + struct tcphdr *otcph, *ntcph; + unsigned int otcplen; + u_int16_t tmp; + + /* A truncated TCP header isn't going to be useful */ + if (oskb->len < (oskb->nh.iph->ihl*4) + sizeof(struct tcphdr)) + return; + + otcph = (struct tcphdr *)((u_int32_t*)oskb->nh.iph + + oskb->nh.iph->ihl); + otcplen = oskb->len - oskb->nh.iph->ihl*4; + + /* No replies for RST or FIN */ + if (otcph->rst || otcph->fin) + return; + + /* No reply to !SYN,!ACK. Rate-limit replies to !SYN,ACKs */ + if (!otcph->syn && (!otcph->ack || !xrlim_allow(&ort->u.dst, 1*HZ))) + return; + + /* Check checksum. */ + if (tcp_v4_check(otcph, otcplen, oskb->nh.iph->saddr, + oskb->nh.iph->daddr, + csum_partial((char *)otcph, otcplen, 0)) != 0) + return; + + /* Copy skb (even if skb is about to be dropped, we can't just + clone it because there may be other things, such as tcpdump, + interested in it) */ + nskb = skb_copy(oskb, GFP_ATOMIC); + if (!nskb) + return; + + /* This packet will not be the same as the other: clear nf fields */ + nf_conntrack_put(nskb->nfct); + nskb->nfct = NULL; + nskb->nfcache = 0; +#ifdef CONFIG_NETFILTER_DEBUG + nskb->nf_debug = 0; +#endif + + ntcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl); + + /* Truncate to length (no data) */ + ntcph->doff = sizeof(struct tcphdr)/4; + skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr)); + nskb->nh.iph->tot_len = htons(nskb->len); + + /* Swap source and dest */ + nskb->nh.iph->daddr = xchg(&nskb->nh.iph->saddr, nskb->nh.iph->daddr); + tmp = ntcph->source; + ntcph->source = ntcph->dest; + ntcph->dest = tmp; + + /* Use supplied sequence number or make a new one */ + ntcph->seq = otcph->ack ? otcph->ack_seq + : htonl(secure_tcp_sequence_number(nskb->nh.iph->saddr, + nskb->nh.iph->daddr, + ntcph->source, + ntcph->dest)); + + /* Our SYN-ACKs must have a >0 window */ + ntcph->window = (otcph->syn && !otcph->ack) ? htons(5) : 0; + + ntcph->urg_ptr = 0; + + /* Reset flags */ + ((u_int8_t *)ntcph)[13] = 0; + + if (otcph->syn && otcph->ack) { + ntcph->rst = 1; + ntcph->ack_seq = 0; + } else { + ntcph->syn = otcph->syn; + ntcph->ack = 1; + ntcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn); + } + + /* Adjust TCP checksum */ + ntcph->check = 0; + ntcph->check = tcp_v4_check(ntcph, sizeof(struct tcphdr), + nskb->nh.iph->saddr, + nskb->nh.iph->daddr, + csum_partial((char *)ntcph, + sizeof(struct tcphdr), 0)); + + /* Adjust IP TTL */ + nskb->nh.iph->ttl = sysctl_ip_default_ttl; + + /* Set DF, id = 0 */ + nskb->nh.iph->frag_off = htons(IP_DF); + nskb->nh.iph->id = 0; + + /* Adjust IP checksum */ + nskb->nh.iph->check = 0; + nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, + nskb->nh.iph->ihl); + + if (ip_route_output(&nrt, nskb->nh.iph->daddr, + local ? nskb->nh.iph->saddr : 0, + RT_TOS(nskb->nh.iph->tos) | RTO_CONN, + 0) != 0) + goto free_nskb; + + dst_release(nskb->dst); + nskb->dst = &nrt->u.dst; + + /* "Never happens" */ + if (nskb->len > nskb->dst->pmtu) + goto free_nskb; + + ip_direct_send (nskb); + + return; + + free_nskb: + kfree_skb(nskb); +} + + +static unsigned int tarpit(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + struct sk_buff *skb = *pskb; + struct rtable *rt = (struct rtable*)skb->dst; + + /* Do we have an input route cache entry? */ + if (!rt) + return NF_DROP; + + /* No replies to physical multicast/broadcast */ + if (skb->pkt_type != PACKET_HOST && skb->pkt_type != PACKET_OTHERHOST) + return NF_DROP; + + /* Now check at the protocol level */ + if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST)) + return NF_DROP; + + /* Our naive response construction doesn't deal with IP + options, and probably shouldn't try. */ + if (skb->nh.iph->ihl*4 != sizeof(struct iphdr)) + return NF_DROP; + + /* We aren't interested in fragments */ + if (skb->nh.iph->frag_off & htons(IP_OFFSET)) + return NF_DROP; + + tarpit_tcp(skb,rt,hooknum == NF_IP_LOCAL_IN); + + return NF_DROP; +} + + +static int check(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + /* Only allow these for input/forward packet filtering. */ + if (strcmp(tablename, "filter") != 0) { + DEBUGP("TARPIT: bad table %s'.\n", tablename); + return 0; + } + if ((hook_mask & ~((1 << NF_IP_LOCAL_IN) + | (1 << NF_IP_FORWARD))) != 0) { + DEBUGP("TARPIT: bad hook mask %X\n", hook_mask); + return 0; + } + + /* Must specify that it's a TCP packet */ + if (e->ip.proto != IPPROTO_TCP || (e->ip.invflags & IPT_INV_PROTO)) { + DEBUGP("TARPIT: not valid for non-tcp\n"); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_tarpit_reg += { { NULL, NULL }, "TARPIT", tarpit, check, NULL, THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_target(&ipt_tarpit_reg)) + return -EINVAL; + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_tarpit_reg); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_TTL.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_TTL.c --- linux-2.4.26/net/ipv4/netfilter/ipt_TTL.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_TTL.c Sun Apr 18 17:33:04 2004 @@ -0,0 +1,110 @@ +/* TTL modification target for IP tables + * (C) 2000 by Harald Welte + * + * Version: 1.2 + * + * 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 -urN linux-2.4.26/net/ipv4/netfilter/ipt_ULOG.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_ULOG.c --- linux-2.4.26/net/ipv4/netfilter/ipt_ULOG.c Wed Jul 30 09:19:06 2003 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_ULOG.c Sun Apr 18 17:33:07 2004 @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,10 @@ MODULE_PARM(flushtimeout, "i"); MODULE_PARM_DESC(flushtimeout, "buffer flush timeout"); +static unsigned int nflog = 1; +MODULE_PARM(nflog, "i"); +MODULE_PARM_DESC(nflog, "register as internal netfilter logging module"); + /* global data structures */ typedef struct { @@ -154,17 +159,17 @@ return skb; } -static unsigned int ipt_ulog_target(struct sk_buff **pskb, - unsigned int hooknum, - const struct net_device *in, - const struct net_device *out, - const void *targinfo, void *userinfo) +static void ipt_ulog_packet(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const struct ipt_ulog_info *loginfo, + const char *prefix) { ulog_buff_t *ub; ulog_packet_msg_t *pm; size_t size, copy_len; struct nlmsghdr *nlh; - struct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) targinfo; /* ffs == find first bit set, necessary because userspace * is already shifting groupnumber, but we need unshifted. @@ -215,7 +220,9 @@ pm->timestamp_usec = (*pskb)->stamp.tv_usec; pm->mark = (*pskb)->nfmark; pm->hook = hooknum; - if (loginfo->prefix[0] != '\0') + if (prefix != NULL) + strncpy(pm->prefix, prefix, sizeof(pm->prefix)); + else if (loginfo->prefix[0] != '\0') strncpy(pm->prefix, loginfo->prefix, sizeof(pm->prefix)); else *(pm->prefix) = '\0'; @@ -262,8 +269,7 @@ UNLOCK_BH(&ulog_lock); - return IPT_CONTINUE; - + return; nlmsg_failure: PRINTR("ipt_ULOG: error during NLMSG_PUT\n"); @@ -272,8 +278,128 @@ PRINTR("ipt_ULOG: Error building netlink message\n"); UNLOCK_BH(&ulog_lock); +} + +static unsigned int ipt_ulog_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, void *userinfo) +{ + struct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) targinfo; + + ipt_ulog_packet(pskb, hooknum, in, out, loginfo, NULL); + + return IPT_CONTINUE; +} + +static void ip_ulog_packet_fn(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ + struct ipt_ulog_info loginfo = { + .nl_group = NFLOG_DEFAULT_NLGROUP, + .copy_range = 0, + .qthreshold = NFLOG_DEFAULT_QTHRESHOLD, + .prefix = "" + }; + + ipt_ulog_packet(pskb, hooknum, in, out, &loginfo, prefix); +} + +static void ip_ulog_fn(char *pfh, size_t len, + const char *prefix) +{ + struct ipt_ulog_info loginfo = { + .nl_group = NFLOG_DEFAULT_NLGROUP, + .copy_range = 0, + .qthreshold = NFLOG_DEFAULT_QTHRESHOLD, + .prefix = "" + }; + ulog_buff_t *ub; + ulog_packet_msg_t *pm; + size_t size; + struct nlmsghdr *nlh; + + /* ffs == find first bit set, necessary because userspace + * is already shifting groupnumber, but we need unshifted. + * ffs() returns [1..32], we need [0..31] */ + unsigned int groupnum = ffs(loginfo.nl_group) - 1; + + size = NLMSG_SPACE(sizeof(*pm) + len); + + ub = &ulog_buffers[groupnum]; + + LOCK_BH(&ulog_lock); + + if (!ub->skb) { + if (!(ub->skb = ulog_alloc_skb(size))) + goto alloc_failure; + } else if (ub->qlen >= loginfo.qthreshold || + size > skb_tailroom(ub->skb)) { + /* either the queue len is too high or we don't have + * enough room in nlskb left. send it to userspace. */ + + ulog_send(groupnum); + + if (!(ub->skb = ulog_alloc_skb(size))) + goto alloc_failure; + } + + DEBUGP("ipt_ULOG: qlen %d, qthreshold %d\n", ub->qlen, + loginfo.qthreshold); + + /* NLMSG_PUT contains a hidden goto nlmsg_failure !!! */ + nlh = NLMSG_PUT(ub->skb, 0, ub->qlen, ULOG_NL_EVENT, + size - sizeof(*nlh)); + ub->qlen++; + + pm = NLMSG_DATA(nlh); + + /* Set fake hook, prefix, timestamp etc. */ + pm->data_len = len; + pm->timestamp_sec = 0; + pm->timestamp_usec = 0; + pm->mark = 0; + pm->hook = 0; + strncpy(pm->prefix, prefix, sizeof(pm->prefix)); + pm->mac_len = 0; + pm->indev_name[0] = '\0'; + pm->outdev_name[0] = '\0'; + memcpy(pm->payload, pfh, len); + + /* check if we are building multi-part messages */ + if (ub->qlen > 1) { + ub->lastnlh->nlmsg_flags |= NLM_F_MULTI; + } + + /* if threshold is reached, send message to userspace */ + if (qlen >= loginfo.qthreshold) { + if (loginfo.qthreshold > 1) + nlh->nlmsg_type = NLMSG_DONE; + } + + ub->lastnlh = nlh; + + /* if timer isn't already running, start it */ + if (!timer_pending(&ub->timer)) { + ub->timer.expires = jiffies + flushtimeout; + add_timer(&ub->timer); + } + + UNLOCK_BH(&ulog_lock); + + return; + +nlmsg_failure: + PRINTR("ipt_ULOG: error during NLMSG_PUT\n"); - return IPT_CONTINUE; +alloc_failure: + PRINTR("ipt_ULOG: Error building netlink message\n"); + + UNLOCK_BH(&ulog_lock); } static int ipt_ulog_checkentry(const char *tablename, @@ -308,6 +434,8 @@ { {NULL, NULL}, "ULOG", ipt_ulog_target, ipt_ulog_checkentry, NULL, THIS_MODULE }; +static struct nf_logging_t ip_logging_fn += { ip_ulog_packet_fn, ip_ulog_fn }; static int __init init(void) { @@ -335,7 +463,9 @@ sock_release(nflognl->socket); return -EINVAL; } - + if (nflog) + nf_ip_log_register(&ip_logging_fn); + return 0; } @@ -346,6 +476,8 @@ DEBUGP("ipt_ULOG: cleanup_module\n"); + if (nflog) + nf_ip_log_unregister(&ip_logging_fn); ipt_unregister_target(&ipt_ulog_reg); sock_release(nflognl->socket); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_XOR.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_XOR.c --- linux-2.4.26/net/ipv4/netfilter/ipt_XOR.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_XOR.c Sun Apr 18 17:33:04 2004 @@ -0,0 +1,107 @@ +/* XOR target for IP tables + * (C) 2000 by Tim Vandermeersch + * Based on ipt_TTL.c + * + * Version 1.0 + * + * This software is distributed under the terms of GNU GPL + */ + +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Tim Vandermeersch "); +MODULE_DESCRIPTION("IP tables XOR module"); +MODULE_LICENSE("GPL"); + +static unsigned int ipt_xor_target(struct sk_buff **pskb, unsigned int hooknum, + const struct net_device *in, const struct net_device *out, + const void *targinfo, void *userinfo) +{ + struct ipt_XOR_info *info = (void *) targinfo; + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph; + struct udphdr *udph; + int i, j, k; + + if (iph->protocol == IPPROTO_TCP) { + tcph = (struct tcphdr *) ((*pskb)->data + iph->ihl*4); + for (i=0, j=0; i<(ntohs(iph->tot_len) - iph->ihl*4 - tcph->doff*4); ) { + for (k=0; k<=info->block_size; k++) { + (char) (*pskb)->data[ iph->ihl*4 + tcph->doff*4 + i ] ^= + info->key[j]; + i++; + } + j++; + if (info->key[j] == 0x00) + j = 0; + } + } else if (iph->protocol == IPPROTO_UDP) { + udph = (struct udphdr *) ((*pskb)->data + iph->ihl*4); + for (i=0, j=0; i<(ntohs(udph->len)-8); ) { + for (k=0; k<=info->block_size; k++) { + (char) (*pskb)->data[ iph->ihl*4 + sizeof(struct udphdr) + i ] ^= + info->key[j]; + i++; + } + j++; + if (info->key[j] == 0x00) + j = 0; + } + } + + return IPT_CONTINUE; +} + +static int ipt_xor_checkentry(const char *tablename, const struct ipt_entry *e, + void *targinfo, unsigned int targinfosize, + unsigned int hook_mask) +{ + struct ipt_XOR_info *info = targinfo; + + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_XOR_info))) { + printk(KERN_WARNING "XOR: targinfosize %u != %Zu\n", + targinfosize, IPT_ALIGN(sizeof(struct ipt_XOR_info))); + return 0; + } + + if (strcmp(tablename, "mangle")) { + printk(KERN_WARNING "XOR: can only be called from" + "\"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + + if (!strcmp(info->key, "")) { + printk(KERN_WARNING "XOR: You must specify a key"); + return 0; + } + + if (info->block_size == 0) { + printk(KERN_WARNING "XOR: You must specify a block-size"); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_XOR = { { NULL, NULL }, "XOR", + ipt_xor_target, ipt_xor_checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_target(&ipt_XOR); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_XOR); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_account.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_account.c --- linux-2.4.26/net/ipv4/netfilter/ipt_account.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_account.c Sun Apr 18 17:33:04 2004 @@ -0,0 +1,617 @@ +/* + * accounting match (ipt_account.c) + * (C) 2003,2004 by Piotr Gasid³o (quaker@barbara.eu.org) + * + * Version: 0.1.5 + * + * This software is distributed under the terms of GNU GPL + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +static char version[] = +KERN_INFO "ipt_account 0.1.5 : Piotr Gasid³o , http://www.barbara.eu.org/~quaker/ipt_account/\n"; + +/* default rights for files created in /proc/net/ipt_account/ */ +static int ip_list_perms = 0644; + +/* + * safe netmask, if you want account traffic for networks + * bigger that /17 you must specify ip_list_max_hosts parameter + * during load + */ +static int ip_list_max_mask = 17; +static int ip_list_max_hosts_count; +static int debug = 0; + +/* module information */ +MODULE_AUTHOR("Piotr Gasid³o "); +MODULE_DESCRIPTION("Traffic accounting modules"); +MODULE_LICENSE("GPL"); +MODULE_PARM(ip_list_perms,"i"); +MODULE_PARM_DESC(ip_list_perms,"permissions on /proc/net/ipt_account/* files"); +MODULE_PARM(ip_list_max_mask, "i"); +MODULE_PARM_DESC(ip_list_max_mask, "maximum *save* size of one list (netmask)"); +#ifdef DEBUG +MODULE_PARM(debug,"i"); +MODULE_PARM_DESC(debug,"debugging level, defaults to 0"); +#endif + +/* structure with statistics counters */ +struct t_ipt_account_stat { + u_int64_t b_all, b_tcp, b_udp, b_icmp, b_other; /* byte counters for all/tcp/udp/icmp/other traffic */ + u_int64_t p_all, p_tcp, p_udp, p_icmp, p_other; /* packet counters for all/tcp/udp/icmp/other traffic */ +}; + +/* structure holding to/from statistics for single ip */ +struct t_ipt_account_ip_list { + struct t_ipt_account_stat src; + struct t_ipt_account_stat dest; +}; + +/* structure describing single table */ +struct t_ipt_account_table { + char name[IPT_ACCOUNT_NAME_LEN]; /* table name ( = filename in /proc/net/ipt_account/) */ + struct t_ipt_account_ip_list *ip_list; /* table with statistics for each ip in network/netmask */ + struct t_ipt_account_table *next; + u_int32_t network; /* network/netmask covered by table*/ + u_int32_t netmask; + int use_count; /* rules counter - counting number of rules using this table */ + spinlock_t ip_list_lock; + struct proc_dir_entry *status_proc_64, *status_proc_32; +}; + +/* we must use spinlocks to avoid parallel modifications of table list */ +static spinlock_t ipt_account_tables_lock = SPIN_LOCK_UNLOCKED; + +static struct proc_dir_entry *proc_net_ipt_account = NULL; + +/* root pointer holding list of the tables */ +static struct t_ipt_account_table *ipt_account_tables = NULL; + +static int ip_list_read_proc_64(char *buffer, char **start, off_t offset, + int length, int *eof, void *data) { + + int len = 0, last_len = 0; + off_t pos = 0, begin = 0; + + u_int32_t address, index; + + struct t_ipt_account_table *table = (struct t_ipt_account_table*)data; + + spin_lock(&table->ip_list_lock); + for (address = table->network; (u_int32_t)(address & table->netmask) == (u_int32_t)(table->network); address++) { + last_len = len; + index = address - table->network; + len += sprintf(buffer + len, + "ip = %u.%u.%u.%u bytes_src = %llu %llu %llu %llu %llu packets_src = %llu %llu %llu %llu %llu bytes_dest = %llu %llu %llu %llu %llu packets_dest = %llu %llu %llu %llu %llu\n", + HIPQUAD(address), + table->ip_list[index].src.b_all, + table->ip_list[index].src.b_tcp, + table->ip_list[index].src.b_udp, + table->ip_list[index].src.b_icmp, + table->ip_list[index].src.b_other, + + table->ip_list[index].src.p_all, + table->ip_list[index].src.p_tcp, + table->ip_list[index].src.p_udp, + table->ip_list[index].src.p_icmp, + table->ip_list[index].src.p_other, + + table->ip_list[index].dest.b_all, + table->ip_list[index].dest.b_tcp, + table->ip_list[index].dest.b_udp, + table->ip_list[index].dest.b_icmp, + table->ip_list[index].dest.b_other, + + table->ip_list[index].dest.p_all, + table->ip_list[index].dest.p_tcp, + table->ip_list[index].dest.p_udp, + table->ip_list[index].dest.p_icmp, + table->ip_list[index].dest.p_other + ); + pos = begin + len; + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) { + len = last_len; + break; + } + } + spin_unlock(&table->ip_list_lock); + *start = buffer + (offset - begin); + len -= (offset - begin); + if (len > length) + len = length; + return len; +} + +static int ip_list_read_proc_32(char *buffer, char **start, off_t offset, + int length, int *eof, void *data) { + + int len = 0, last_len = 0; + off_t pos = 0, begin = 0; + + u_int32_t address, index; + + struct t_ipt_account_table *table = (struct t_ipt_account_table*)data; + + spin_lock(&table->ip_list_lock); + for (address = table->network; (u_int32_t)(address & table->netmask) == (u_int32_t)(table->network); address++) { + last_len = len; + index = address - table->network; + len += sprintf(buffer + len, + "ip = %u.%u.%u.%u bytes_src = %u %u %u %u %u packets_src = %u %u %u %u %u bytes_dest = %u %u %u %u %u packets_dest = %u %u %u %u %u\n", + HIPQUAD(address), + (u_int32_t)table->ip_list[index].src.b_all, + (u_int32_t)table->ip_list[index].src.b_tcp, + (u_int32_t)table->ip_list[index].src.b_udp, + (u_int32_t)table->ip_list[index].src.b_icmp, + (u_int32_t)table->ip_list[index].src.b_other, + + (u_int32_t)table->ip_list[index].src.p_all, + (u_int32_t)table->ip_list[index].src.p_tcp, + (u_int32_t)table->ip_list[index].src.p_udp, + (u_int32_t)table->ip_list[index].src.p_icmp, + (u_int32_t)table->ip_list[index].src.p_other, + + (u_int32_t)table->ip_list[index].dest.b_all, + (u_int32_t)table->ip_list[index].dest.b_tcp, + (u_int32_t)table->ip_list[index].dest.b_udp, + (u_int32_t)table->ip_list[index].dest.b_icmp, + (u_int32_t)table->ip_list[index].dest.b_other, + + (u_int32_t)table->ip_list[index].dest.p_all, + (u_int32_t)table->ip_list[index].dest.p_tcp, + (u_int32_t)table->ip_list[index].dest.p_udp, + (u_int32_t)table->ip_list[index].dest.p_icmp, + (u_int32_t)table->ip_list[index].dest.p_other + + ); + pos = begin + len; + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) { + len = last_len; + break; + } + } + spin_unlock(&table->ip_list_lock); + *start = buffer + (offset - begin); + len -= (offset - begin); + if (len > length) + len = length; + return len; +} + +static int ip_list_write_proc(struct file *file, const char *buffer, + unsigned long length, void *data) { + + int len = (length > 1024) ? length : 1024; + struct t_ipt_account_table *table = (struct t_ipt_account_table*)data; + char kernel_buffer[1024]; + u_int32_t hosts_count = INADDR_BROADCAST - table->netmask + 1; + + copy_from_user(kernel_buffer, buffer, len); + kernel_buffer[len - 1] = 0; + + /* echo "reset" > /proc/net/ipt_recent/table clears the table */ + if (!strncmp(kernel_buffer, "reset", len)) { + spin_lock(&table->ip_list_lock); + memset(table->ip_list, 0, sizeof(struct t_ipt_account_ip_list) * hosts_count); + spin_unlock(&table->ip_list_lock); + } + + return len; +} + +/* do raw accounting */ +static void do_account(struct t_ipt_account_stat *stat, u_int8_t proto, u_int16_t pktlen) { + + /* update packet & bytes counters in *stat structure */ + stat->b_all += pktlen; + stat->p_all++; + + switch (proto) { + case IPPROTO_TCP: + stat->b_tcp += pktlen; + stat->p_tcp++; + break; + case IPPROTO_UDP: + stat->b_udp += pktlen; + stat->p_udp++; + break; + case IPPROTO_ICMP: + stat->b_icmp += pktlen; + stat->p_icmp++; + break; + default: + stat->b_other += pktlen; + stat->p_other++; + } +} + +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 t_ipt_account_info *info = (struct t_ipt_account_info*)matchinfo; + struct t_ipt_account_table *table; + int ret; + + u_int32_t address; + u_int16_t pktlen; + u_int8_t proto; + + if (debug) { + printk(KERN_INFO "ipt_account: match() entering.\n"); + printk(KERN_INFO "ipt_account: match() match name = %s.\n", info->name); + } + + spin_lock(&ipt_account_tables_lock); + /* find the right table */ + table = ipt_account_tables; + while (table && strncmp(table->name, info->name, IPT_ACCOUNT_NAME_LEN) && (table = table->next)); + spin_unlock(&ipt_account_tables_lock); + + if (table == NULL) { + /* ups, no table with that name */ + if (debug) + printk(KERN_INFO "ipt_account: match() table %s not found. Leaving.\n", info->name); + return 0; + } + + if (debug) + printk(KERN_INFO "ipt_account: match() table found %s\n", table->name); + + /* default: no match */ + ret = 0; + + /* get packet protocol/length */ + pktlen = skb->len; + proto = skb->nh.iph->protocol; + + if (debug) + printk(KERN_INFO "ipt_account: match() got packet src = %u.%u.%u.%u, dst = %u.%u.%u.%u, proto = %u.\n", + NIPQUAD(skb->nh.iph->saddr), + NIPQUAD(skb->nh.iph->daddr), + proto + ); + + /* check whether traffic from source ip address ... */ + address = ntohl(skb->nh.iph->saddr); + + /* ... is being accounted by this table */ + if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) { + if (debug) + printk(KERN_INFO "ipt_account: match() accounting packet src = %u.%u.%u.%u, proto = %u.\n", + HIPQUAD(address), + proto + ); + /* yes, account this packet */ + spin_lock(&table->ip_list_lock); + /* update counters this host */ + do_account(&table->ip_list[(u_int32_t)(address - table->network)].src, proto, pktlen); + /* update counters for all hosts in this table (network address) */ + if (table->netmask != INADDR_BROADCAST) + do_account(&table->ip_list[0].src, proto, pktlen); + spin_unlock(&table->ip_list_lock); + /* yes, it's a match */ + ret = 1; + } + + /* do the same thing with destination ip address */ + address = ntohl(skb->nh.iph->daddr); + if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) { + if (debug) + printk(KERN_INFO "ipt_account: match() accounting packet dst = %u.%u.%u.%u, proto = %u.\n", + HIPQUAD(address), + proto + ); + spin_lock(&table->ip_list_lock); + do_account(&table->ip_list[(u_int32_t)(address - table->network)].dest, proto, pktlen); + if (table->netmask != INADDR_BROADCAST) + do_account(&table->ip_list[0].dest, proto, pktlen); + spin_unlock(&table->ip_list_lock); + ret = 1; + + } + + if (debug) + printk(KERN_INFO "ipt_account: match() leaving.\n"); + + return ret; + +} + +static int checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchinfosize, + unsigned int hook_mask) +{ + const struct t_ipt_account_info *info = matchinfo; + struct t_ipt_account_table *table; + + char proc_entry_name[IPT_ACCOUNT_NAME_LEN + 3]; + + u_int32_t hosts_count; + + if (debug) + printk(KERN_INFO "ipt_account: checkentry() entering.\n"); + + if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) + return 0; + + if (!info->name || !info->name[0]) + return 0; + + /* find whether table with this name already exists */ + spin_lock(&ipt_account_tables_lock); + table = ipt_account_tables; + while (table && strncmp(info->name, table->name, IPT_ACCOUNT_NAME_LEN) && (table = table->next)); + + if (table) { + /* yes, table exists */ + if (info->network != table->network || info->netmask != table->netmask) { + /* + * tried to do accounting in existing table, but network/netmask in iptable rule + * doesn't match network/netmask in table structure - deny adding the rule + */ + printk(KERN_ERR "ipt_account: checkentry() table %s found. But table netmask/network %u.%u.%u.%u/%u.%u.%u.%u differs from rule netmask/network %u.%u.%u.%u/%u.%u.%u.%u. Leaving without creating entry.\n", + table->name, + HIPQUAD(table->network), + HIPQUAD(table->netmask), + HIPQUAD(info->network), + HIPQUAD(info->netmask) + ); + spin_unlock(&ipt_account_tables_lock); + return 0; + } + if (debug) + printk(KERN_INFO "ipt_account: checkentry() table %s found. Incrementing use count (use_count = %i). Leaving.\n", table->name, table->use_count); + /* increase table use count */ + table->use_count++; + spin_unlock(&ipt_account_tables_lock); + /* everything went okey */ + return 1; + }; + + if (debug) + printk(KERN_INFO "ipt_account: checkentry() table %s not found. Creating.\n", info->name); + + /* table doesn't exist - create one */ + table = vmalloc(sizeof(struct t_ipt_account_table)); + if (table == NULL) { + if (debug) + printk(KERN_INFO "ipt_account: checkentry() unable to allocate memory (t_account_table) for table %s. Leaving.\n", info->name); + spin_unlock(&ipt_account_tables_lock); + return -ENOMEM; + } + + /* set table parameters */ + strncpy(table->name, info->name, IPT_ACCOUNT_NAME_LEN); + table->use_count = 1; + table->network = info->network; + table->netmask = info->netmask; + table->ip_list_lock = SPIN_LOCK_UNLOCKED; + + hosts_count = INADDR_BROADCAST - table->netmask + 1; + + if (debug) + printk(KERN_INFO "ipt_account: checkentry() allocating memory for %u hosts (%u netmask).\n", hosts_count, info->netmask); + + /* check whether table is not too big */ + if (hosts_count > ip_list_max_hosts_count) { + printk(KERN_ERR "ipt_account: checkentry() unable allocate memory for %u hosts (%u netmask). Increase value of ip_list_max_mask parameter.\n", hosts_count, info->netmask); + vfree(table); + spin_unlock(&ipt_account_tables_lock); + return -ENOMEM; + } + + table->ip_list = vmalloc(sizeof(struct t_ipt_account_ip_list) * hosts_count); + if (table->ip_list == NULL) { + if (debug) + printk(KERN_INFO "ipt_account: checkentry() unable to allocate memory (t_account_ip_list) for table %s. Leaving.\n", table->name); + vfree(table); + spin_unlock(&ipt_account_tables_lock); + return -ENOMEM; + } + + memset(table->ip_list, 0, sizeof(struct t_ipt_account_ip_list) * hosts_count); + + /* + * create entries in /proc/net/ipt_account: one with full 64-bit counters and + * second with 32-bit ones. The second can be used in programs supporting only 32-bit numbers + * (mrtg, rrdtool). + */ + + strncpy(proc_entry_name, table->name, IPT_ACCOUNT_NAME_LEN); + strncat(proc_entry_name, "_64", 4); + + table->status_proc_64 = create_proc_entry(proc_entry_name, ip_list_perms, proc_net_ipt_account); + if (table->status_proc_64 == NULL) { + if (debug) + printk(KERN_INFO "ipt_account: checkentry() unable to allocate memory (status_proc_64) for table %s. Leaving.\n", table->name); + vfree(table->ip_list); + vfree(table); + spin_unlock(&ipt_account_tables_lock); + return -ENOMEM; + } + + table->status_proc_64->owner = THIS_MODULE; + table->status_proc_64->read_proc = ip_list_read_proc_64; + table->status_proc_64->write_proc = ip_list_write_proc; + table->status_proc_64->data = table; + + strncpy(proc_entry_name, table->name, IPT_ACCOUNT_NAME_LEN); + strncat(proc_entry_name, "_32", 4); + + table->status_proc_32 = create_proc_entry(proc_entry_name, ip_list_perms, proc_net_ipt_account); + if (table->status_proc_32 == NULL) { + if (debug) + printk(KERN_INFO "ipt_account: checkentry() unable to allocate memory (status_proc_32) for table %s. Leaving.\n", table->name); + vfree(table->ip_list); + vfree(table); + spin_unlock(&ipt_account_tables_lock); + return -ENOMEM; + } + + table->status_proc_32->owner = THIS_MODULE; + table->status_proc_32->read_proc = ip_list_read_proc_32; + table->status_proc_32->write_proc = ip_list_write_proc; + table->status_proc_32->data = table; + + /* finaly, insert table into list */ + table->next = ipt_account_tables; + ipt_account_tables = table; + + if (debug) + printk(KERN_INFO "ipt_account: checkentry() successfully created table %s (use_count = %i).\n", table->name, table->use_count); + + spin_unlock(&ipt_account_tables_lock); + + if (debug) + printk(KERN_INFO "ipt_account: checkentry() leaving.\n"); + return 1; +} + +static void destroy(void *matchinfo, + unsigned int matchinfosize) +{ + const struct t_ipt_account_info *info = matchinfo; + struct t_ipt_account_table *table, *last_table; + char proc_entry_name[IPT_ACCOUNT_NAME_LEN + 3]; + + if (debug) + printk(KERN_INFO "ipt_account: destroy() entered.\n"); + + if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) + return; + + spin_lock(&ipt_account_tables_lock); + table = ipt_account_tables; + + if (table == NULL) { + /* list is empty, sometheing is realy wrong! */ + if (debug) + printk(KERN_INFO "ipt_account: destroy() unable to found any tables (asked for %s). Leaving.\n", info->name); + spin_unlock(&ipt_account_tables_lock); + return; + } + + /* find table combined with this rule - this code is taken for ipt_recent ;) */ + last_table = NULL; + while (strncmp(table->name, info->name, IPT_ACCOUNT_NAME_LEN) && (last_table = table) && (table = table->next)); + + if (table == NULL) { + printk(KERN_ERR "ipt_account: destroy() unable to found table %s. Leaving.\n", info->name); + spin_unlock(&ipt_account_tables_lock); + return; + } + + /* decrease table use counter */ + table->use_count--; + if (table->use_count != 0) { + /* table is used by other rule, can't remove it */ + if (debug) + printk(KERN_INFO "ipt_account: destroy() table %s is still used (use_count = %i). Leaving.\n", table->name, table->use_count); + spin_unlock(&ipt_account_tables_lock); + return; + } + + /* table is not used by any other tule - remove it */ + if (debug) + printk(KERN_INFO "ipt_account: destroy() removing table %s (use_count = %i).\n", table->name, table->use_count); + + if (last_table) + last_table->next = table->next; + else + ipt_account_tables = table->next; + + spin_lock(&table->ip_list_lock); + spin_unlock(&table->ip_list_lock); + + /* remove procfs entries */ + strncpy(proc_entry_name, table->name, IPT_ACCOUNT_NAME_LEN); + strncat(proc_entry_name, "_64", 4); + remove_proc_entry(proc_entry_name, proc_net_ipt_account); + strncpy(proc_entry_name, table->name, IPT_ACCOUNT_NAME_LEN); + strncat(proc_entry_name, "_32", 4); + remove_proc_entry(proc_entry_name, proc_net_ipt_account); + vfree(table->ip_list); + vfree(table); + + spin_unlock(&ipt_account_tables_lock); + + if (debug) + printk(KERN_INFO "account: destroy() leaving.\n"); + + return; +} + +static struct ipt_match account_match = { + { NULL, NULL }, + "account", + &match, + &checkentry, + &destroy, + THIS_MODULE +}; + +static int __init init(void) +{ + printk(version); + if (debug) + printk(KERN_INFO "account: __init(): ip_list_perms = %i, ip_list_max_mask = %i\n", ip_list_perms, ip_list_max_mask); + /* check params */ + if (ip_list_max_mask > 32 || ip_list_max_mask < 0) { + printk(KERN_ERR "account: Wrong netmask given by ip_list_max_mask parameter (%u). Valid is 32 to 0.\n", ip_list_max_mask); + return 0; + } + + ip_list_max_hosts_count = (1 << (32 - ip_list_max_mask)) + 1; + + /* create /proc/net/ipt_account directory */ + proc_net_ipt_account = proc_mkdir("ipt_account", proc_net); + if (!proc_net_ipt_account) + return -ENOMEM; + + return ipt_register_match(&account_match); +} + +/* procedura usuwaj±ca modu³ */ +static void __exit fini(void) +{ + ipt_unregister_match(&account_match); + /* remove /proc/net/ipt_account/ directory */ + remove_proc_entry("ipt_account", proc_net); +} + +module_init(init); +module_exit(fini); + diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_addrtype.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_addrtype.c --- linux-2.4.26/net/ipv4/netfilter/ipt_addrtype.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_addrtype.c Sun Apr 18 17:33:05 2004 @@ -0,0 +1,65 @@ +/* + * iptables module to match inet_addr_type() of an ip. + */ + +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +static inline int match_type(u_int32_t addr, u_int16_t mask) +{ + return !!(mask & (1 << inet_addr_type(addr))); +} + +static int match(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const void *matchinfo, + int offset, const void *hdr, u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_addrtype_info *info = matchinfo; + const struct iphdr *iph = skb->nh.iph; + int ret = 1; + + if (info->source) + ret &= match_type(iph->saddr, info->source)^info->invert_source; + if (info->dest) + ret &= match_type(iph->daddr, info->dest)^info->invert_dest; + + return ret; +} + +static int checkentry(const char *tablename, const struct ipt_ip *ip, + void *matchinfo, unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_addrtype_info))) { + printk(KERN_ERR "ipt_addrtype: invalid size (%u != %u)\n.", + matchsize, IPT_ALIGN(sizeof(struct ipt_addrtype_info))); + return 0; + } + + return 1; +} + +static struct ipt_match addrtype_match = { { NULL, NULL }, "addrtype", &match, + &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&addrtype_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&addrtype_match); + +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_condition.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_condition.c --- linux-2.4.26/net/ipv4/netfilter/ipt_condition.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_condition.c Sun Apr 18 17:33:05 2004 @@ -0,0 +1,256 @@ +/*-------------------------------------------*\ +| Netfilter Condition Module | +| | +| Description: This module allows firewall | +| rules to match using condition variables | +| stored in /proc files. | +| | +| Author: Stephane Ouellette 2002-10-22 | +| | +| | +| History: | +| 2003-02-10 Second version with improved | +| locking and simplified code. | +| | +| This software is distributed under the | +| terms of the GNU GPL. | +\*-------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include + + +#ifndef CONFIG_PROC_FS +#error "Proc file system support is required for this module" +#endif + + +MODULE_AUTHOR("Stephane Ouellette "); +MODULE_DESCRIPTION("Allows rules to match against condition variables"); +MODULE_LICENSE("GPL"); + + +struct condition_variable { + struct condition_variable *next; + struct proc_dir_entry *status_proc; + atomic_t refcount; + int enabled; /* TRUE == 1, FALSE == 0 */ +}; + + +static rwlock_t list_lock; +static struct condition_variable *head = NULL; +static struct proc_dir_entry *proc_net_condition = NULL; + + +static int +ipt_condition_read_info(char *buffer, char **start, off_t offset, + int length, int *eof, void *data) +{ + struct condition_variable *var = + (struct condition_variable *) data; + + if (offset == 0) { + *start = buffer; + buffer[0] = (var->enabled) ? '1' : '0'; + buffer[1] = '\n'; + return 2; + } + + *eof = 1; + return 0; +} + + +static int +ipt_condition_write_info(struct file *file, const char *buffer, + unsigned long length, void *data) +{ + struct condition_variable *var = + (struct condition_variable *) data; + + if (length) { + /* Match only on the first character */ + switch (buffer[0]) { + case '0': + var->enabled = 0; + break; + case '1': + var->enabled = 1; + } + } + + return (int) length; +} + + +static int +match(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const void *matchinfo, int offset, + const void *hdr, u_int16_t datalen, int *hotdrop) +{ + const struct condition_info *info = + (const struct condition_info *) matchinfo; + struct condition_variable *var; + int condition_status = 0; + + read_lock(&list_lock); + + for (var = head; var; var = var->next) { + if (strcmp(info->name, var->status_proc->name) == 0) { + condition_status = var->enabled; + break; + } + } + + read_unlock(&list_lock); + + return condition_status ^ info->invert; +} + + + +static int +checkentry(const char *tablename, const struct ipt_ip *ip, + void *matchinfo, unsigned int matchsize, unsigned int hook_mask) +{ + struct condition_info *info = (struct condition_info *) matchinfo; + struct condition_variable *var, *newvar; + + if (matchsize != IPT_ALIGN(sizeof(struct condition_info))) + return 0; + + /* The first step is to check if the condition variable already exists. */ + /* Here, a read lock is sufficient because we won't change the list */ + read_lock(&list_lock); + + for (var = head; var; var = var->next) { + if (strcmp(info->name, var->status_proc->name) == 0) { + atomic_inc(&var->refcount); + read_unlock(&list_lock); + return 1; + } + } + + read_unlock(&list_lock); + + /* At this point, we need to allocate a new condition variable */ + newvar = kmalloc(sizeof(struct condition_variable), GFP_KERNEL); + + if (!newvar) + return -ENOMEM; + + /* Create the condition variable's proc file entry */ + newvar->status_proc = create_proc_entry(info->name, 0644, proc_net_condition); + + if (!newvar->status_proc) { + /* + * There are two possibilities: + * 1- Another condition variable with the same name has been created, which is valid. + * 2- There was a memory allocation error. + */ + kfree(newvar); + read_lock(&list_lock); + + for (var = head; var; var = var->next) { + if (strcmp(info->name, var->status_proc->name) == 0) { + atomic_inc(&var->refcount); + read_unlock(&list_lock); + return 1; + } + } + + read_unlock(&list_lock); + return -ENOMEM; + } + + atomic_set(&newvar->refcount, 1); + newvar->enabled = 0; + newvar->status_proc->owner = THIS_MODULE; + newvar->status_proc->data = newvar; + wmb(); + newvar->status_proc->read_proc = ipt_condition_read_info; + newvar->status_proc->write_proc = ipt_condition_write_info; + + write_lock(&list_lock); + + newvar->next = head; + head = newvar; + + write_unlock(&list_lock); + + return 1; +} + + +static void +destroy(void *matchinfo, unsigned int matchsize) +{ + struct condition_info *info = (struct condition_info *) matchinfo; + struct condition_variable *var, *prev = NULL; + + if (matchsize != IPT_ALIGN(sizeof(struct condition_info))) + return; + + write_lock(&list_lock); + + for (var = head; var && strcmp(info->name, var->status_proc->name); + prev = var, var = var->next); + + if (var && atomic_dec_and_test(&var->refcount)) { + if (prev) + prev->next = var->next; + else + head = var->next; + + write_unlock(&list_lock); + remove_proc_entry(var->status_proc->name, proc_net_condition); + kfree(var); + } else + write_unlock(&list_lock); +} + + +static struct ipt_match condition_match = { + .name = "condition", + .match = &match, + .checkentry = &checkentry, + .destroy = &destroy, + .me = THIS_MODULE +}; + + +static int __init +init(void) +{ + int errorcode; + + rwlock_init(&list_lock); + proc_net_condition = proc_mkdir("ipt_condition", proc_net); + + if (proc_net_condition) { + errorcode = ipt_register_match(&condition_match); + + if (errorcode) + remove_proc_entry("ipt_condition", proc_net); + } else + errorcode = -EACCES; + + return errorcode; +} + + +static void __exit +fini(void) +{ + ipt_unregister_match(&condition_match); + remove_proc_entry("ipt_condition", proc_net); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_connlimit.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_connlimit.c --- linux-2.4.26/net/ipv4/netfilter/ipt_connlimit.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_connlimit.c Sun Apr 18 17:33:05 2004 @@ -0,0 +1,227 @@ +/* + * netfilter module to limit the number of parallel tcp + * connections per IP address. + * (c) 2000 Gerd Knorr + * Nov 2002: Martin Bene : + * only ignore TIME_WAIT or gone connections + * + * based on ... + * + * Kernel module to match connection tracking information. + * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au). + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 0 + +MODULE_LICENSE("GPL"); + +/* we'll save the tuples of all connections we care about */ +struct ipt_connlimit_conn +{ + struct list_head list; + struct ip_conntrack_tuple tuple; +}; + +struct ipt_connlimit_data { + spinlock_t lock; + struct list_head iphash[256]; +}; + +static int ipt_iphash(u_int32_t addr) +{ + int hash; + + hash = addr & 0xff; + hash ^= (addr >> 8) & 0xff; + hash ^= (addr >> 16) & 0xff; + hash ^= (addr >> 24) & 0xff; + return hash; +} + +static int count_them(struct ipt_connlimit_data *data, + u_int32_t addr, u_int32_t mask, + struct ip_conntrack *ct) +{ +#if DEBUG + const static char *tcp[] = { "none", "established", "syn_sent", "syn_recv", + "fin_wait", "time_wait", "close", "close_wait", + "last_ack", "listen" }; +#endif + int addit = 1, matches = 0; + struct ip_conntrack_tuple tuple; + struct ip_conntrack_tuple_hash *found; + struct ipt_connlimit_conn *conn; + struct list_head *hash,*lh; + + spin_lock(&data->lock); + tuple = ct->tuplehash[0].tuple; + hash = &data->iphash[ipt_iphash(addr & mask)]; + + /* check the saved connections */ + for (lh = hash->next; lh != hash; lh = lh->next) { + conn = list_entry(lh,struct ipt_connlimit_conn,list); + found = ip_conntrack_find_get(&conn->tuple,ct); + if (0 == memcmp(&conn->tuple,&tuple,sizeof(tuple)) && + found != NULL && + found->ctrack->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT) { + /* Just to be sure we have it only once in the list. + We should'nt see tuples twice unless someone hooks this + into a table without "-p tcp --syn" */ + addit = 0; + } +#if DEBUG + printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d %s\n", + ipt_iphash(addr & mask), + NIPQUAD(conn->tuple.src.ip), ntohs(conn->tuple.src.u.tcp.port), + NIPQUAD(conn->tuple.dst.ip), ntohs(conn->tuple.dst.u.tcp.port), + (NULL != found) ? tcp[found->ctrack->proto.tcp.state] : "gone"); +#endif + if (NULL == found) { + /* this one is gone */ + lh = lh->prev; + list_del(lh->next); + kfree(conn); + continue; + } + if (found->ctrack->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT) { + /* we don't care about connections which are + closed already -> ditch it */ + lh = lh->prev; + list_del(lh->next); + kfree(conn); + nf_conntrack_put(&found->ctrack->infos[0]); + continue; + } + if ((addr & mask) == (conn->tuple.src.ip & mask)) { + /* same source IP address -> be counted! */ + matches++; + } + nf_conntrack_put(&found->ctrack->infos[0]); + } + if (addit) { + /* save the new connection in our list */ +#if DEBUG + printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d new\n", + ipt_iphash(addr & mask), + NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port), + NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port)); +#endif + conn = kmalloc(sizeof(*conn),GFP_ATOMIC); + if (NULL == conn) + return -1; + memset(conn,0,sizeof(*conn)); + INIT_LIST_HEAD(&conn->list); + conn->tuple = tuple; + list_add(&conn->list,hash); + matches++; + } + spin_unlock(&data->lock); + return matches; +} + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_connlimit_info *info = matchinfo; + int connections, match; + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + if (NULL == ct) { + printk("ipt_connlimit: Oops: invalid ct state ?\n"); + *hotdrop = 1; + return 0; + } + connections = count_them(info->data,skb->nh.iph->saddr,info->mask,ct); + if (-1 == connections) { + printk("ipt_connlimit: Hmm, kmalloc failed :-(\n"); + *hotdrop = 1; /* let's free some memory :-) */ + return 0; + } + match = (info->inverse) ? (connections <= info->limit) : (connections > info->limit); +#if DEBUG + printk("ipt_connlimit: src=%u.%u.%u.%u mask=%u.%u.%u.%u " + "connections=%d limit=%d match=%s\n", + NIPQUAD(skb->nh.iph->saddr), NIPQUAD(info->mask), + connections, info->limit, match ? "yes" : "no"); +#endif + + return match; +} + +static int check(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_connlimit_info *info = matchinfo; + int i; + + /* verify size */ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_connlimit_info))) + return 0; + + /* refuse anything but tcp */ + if (ip->proto != IPPROTO_TCP) + return 0; + + /* init private data */ + info->data = kmalloc(sizeof(struct ipt_connlimit_data),GFP_KERNEL); + spin_lock_init(&(info->data->lock)); + for (i = 0; i < 256; i++) + INIT_LIST_HEAD(&(info->data->iphash[i])); + + return 1; +} + +static void destroy(void *matchinfo, unsigned int matchinfosize) +{ + struct ipt_connlimit_info *info = matchinfo; + struct ipt_connlimit_conn *conn; + struct list_head *hash; + int i; + + /* cleanup */ + for (i = 0; i < 256; i++) { + hash = &(info->data->iphash[i]); + while (hash != hash->next) { + conn = list_entry(hash->next,struct ipt_connlimit_conn,list); + list_del(hash->next); + kfree(conn); + } + } + kfree(info->data); +} + +static struct ipt_match connlimit_match += { { NULL, NULL }, "connlimit", &match, &check, &destroy, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&connlimit_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&connlimit_match); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_helper.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_helper.c --- linux-2.4.26/net/ipv4/netfilter/ipt_helper.c Thu Feb 5 17:30:55 2004 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_helper.c Sun Apr 18 17:33:06 2004 @@ -37,17 +37,17 @@ struct ip_conntrack_expect *exp; struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; - int ret = 0; + int ret = info->invert; ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); if (!ct) { DEBUGP("ipt_helper: Eek! invalid conntrack?\n"); - return 0; + return ret; } if (!ct->master) { DEBUGP("ipt_helper: conntrack %p has no master\n", ct); - return 0; + return ret; } exp = ct->master; @@ -67,8 +67,8 @@ DEBUGP("master's name = %s , info->name = %s\n", exp->expectant->helper->name, info->name); - ret = !strncmp(exp->expectant->helper->name, info->name, - strlen(exp->expectant->helper->name)) ^ info->invert; + ret ^= !strncmp(exp->expectant->helper->name, info->name, + strlen(exp->expectant->helper->name)); out_unlock: READ_UNLOCK(&ip_conntrack_lock); return ret; diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_iprange.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_iprange.c --- linux-2.4.26/net/ipv4/netfilter/ipt_iprange.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_iprange.c Sun Apr 18 17:33:06 2004 @@ -0,0 +1,101 @@ +/* + * iptables module to match IP address ranges + * (c) 2003 Jozsef Kadlecsik + * + * Released under the terms of GNU GPLv2. + * + */ +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("iptables arbitrary IP range match module"); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_iprange_info *info = matchinfo; + const struct iphdr *iph = skb->nh.iph; + + + if (info->flags & IPRANGE_SRC) { + if (((ntohl(iph->saddr) < ntohl(info->src.min_ip)) + || (ntohl(iph->saddr) > ntohl(info->src.max_ip))) + ^ !!(info->flags & IPRANGE_SRC_INV)) { + DEBUGP("src IP %u.%u.%u.%u NOT in range %s" + "%u.%u.%u.%u-%u.%u.%u.%u\n", + NIPQUAD(iph->saddr), + info->flags & IPRANGE_SRC_INV ? "(INV) " : "", + NIPQUAD(info->src.min_ip), + NIPQUAD(info->src.max_ip)); + return 0; + } + } + if (info->flags & IPRANGE_DST) { + if (((ntohl(iph->daddr) < ntohl(info->dst.min_ip)) + || (ntohl(iph->daddr) > ntohl(info->dst.max_ip))) + ^ !!(info->flags & IPRANGE_DST_INV)) { + DEBUGP("dst IP %u.%u.%u.%u NOT in range %s" + "%u.%u.%u.%u-%u.%u.%u.%u\n", + NIPQUAD(iph->daddr), + info->flags & IPRANGE_DST_INV ? "(INV) " : "", + NIPQUAD(info->dst.min_ip), + NIPQUAD(info->dst.max_ip)); + return 0; + } + } + return 1; +} + +static int check(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + /* verify size */ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_iprange_info))) + return 0; + + return 1; +} + +static struct ipt_match iprange_match = +{ + .list = { NULL, NULL }, + .name = "iprange", + .match = &match, + .checkentry = &check, + .destroy = NULL, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_match(&iprange_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&iprange_match); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_ipv4options.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_ipv4options.c --- linux-2.4.26/net/ipv4/netfilter/ipt_ipv4options.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_ipv4options.c Sun Apr 18 17:33:06 2004 @@ -0,0 +1,168 @@ +/* + 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) +{ + return ipt_register_match(&ipv4options_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipv4options_match); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_mport.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_mport.c --- linux-2.4.26/net/ipv4/netfilter/ipt_mport.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_mport.c Sun Apr 18 17:33:07 2004 @@ -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->invflags & IPT_INV_PROTO) + && matchsize == IPT_ALIGN(sizeof(struct ipt_mport)); +} + +static struct ipt_match mport_match += { { NULL, NULL }, "mport", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&mport_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&mport_match); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_nth.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_nth.c --- linux-2.4.26/net/ipv4/netfilter/ipt_nth.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_nth.c Sun Apr 18 17:33:07 2004 @@ -0,0 +1,167 @@ +/* + 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"); +MODULE_AUTHOR("Fabrice Marie "); + +/* + * State information. + */ +struct state { + spinlock_t lock; + u_int16_t number; +}; + +static struct state states[IPT_NTH_NUM_COUNTERS]; + +static int +ipt_nth_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + 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)); + for (counter = 0; counter < IPT_NTH_NUM_COUNTERS; counter++) + spin_lock_init(&(states[counter].lock)); + + return ipt_register_match(&ipt_nth_reg); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipt_nth_reg); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_osf.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_osf.c --- linux-2.4.26/net/ipv4/netfilter/ipt_osf.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_osf.c Sun Apr 18 17:33:08 2004 @@ -0,0 +1,865 @@ +/* + * ipt_osf.c + * + * Copyright (c) 2003 Evgeniy Polyakov + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * OS fingerprint matching module. + * It simply compares various parameters from SYN packet with + * some hardcoded ones. + * + * Original table was created by Michal Zalewski + * for his p0f. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#define OSF_DEBUG + +#ifdef OSF_DEBUG +#define log(x...) printk(KERN_INFO "ipt_osf: " x) +#define loga(x...) printk(x) +#else +#define log(x...) do {} while(0) +#define loga(x...) do {} while(0) +#endif + +#define FMATCH_WRONG 0 +#define FMATCH_OK 1 +#define FMATCH_OPT_WRONG 2 + +#define OPTDEL ',' +#define OSFPDEL ':' +#define MAXOPTSTRLEN 128 +#define OSFFLUSH "FLUSH" + +static rwlock_t osf_lock = RW_LOCK_UNLOCKED; +static spinlock_t ipt_osf_netlink_lock = SPIN_LOCK_UNLOCKED; +static struct list_head finger_list; +static int match(const struct sk_buff *, const struct net_device *, const struct net_device *, + const void *, int, + const void *, u_int16_t, + int *); +static int checkentry(const char *, const struct ipt_ip *, void *, + unsigned int, unsigned int); + +static unsigned long seq, ipt_osf_groups = 1; +static struct sock *nts; + +static struct ipt_match osf_match = +{ + { NULL, NULL }, + "osf", + &match, + &checkentry, + NULL, + THIS_MODULE +}; + +static void ipt_osf_nlsend(struct osf_finger *f, const struct sk_buff *sk) +{ + unsigned int size; + struct sk_buff *skb; + struct ipt_osf_nlmsg *data; + struct nlmsghdr *nlh; + + size = NLMSG_SPACE(sizeof(struct ipt_osf_nlmsg)); + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) + { + log("skb_alloc() failed.\n"); + return; + } + + nlh = NLMSG_PUT(skb, 0, seq++, NLMSG_DONE, size - sizeof(*nlh)); + + data = (struct ipt_osf_nlmsg *)NLMSG_DATA(nlh); + + memcpy(&data->f, f, sizeof(struct osf_finger)); + memcpy(&data->ip, sk->nh.iph, sizeof(struct iphdr)); + memcpy(&data->tcp, (struct tcphdr *)((u_int32_t *)sk->nh.iph + sk->nh.iph->ihl), sizeof(struct tcphdr)); + + NETLINK_CB(skb).dst_groups = ipt_osf_groups; + netlink_broadcast(nts, skb, 0, ipt_osf_groups, GFP_ATOMIC); + +nlmsg_failure: + return; +} + +static inline int smart_dec(const struct sk_buff *skb, unsigned long flags, unsigned char f_ttl) +{ + struct iphdr *ip = skb->nh.iph; + + if (flags & IPT_OSF_SMART) + { + struct in_device *in_dev = in_dev_get(skb->dev); + + for_ifa(in_dev) + { + if (inet_ifa_match(ip->saddr, ifa)) + { + in_dev_put(in_dev); + return (ip->ttl == f_ttl); + } + } + endfor_ifa(in_dev); + + in_dev_put(in_dev); + return (ip->ttl <= f_ttl); + } + else + return (ip->ttl == f_ttl); +} + +static int +match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, + const void *matchinfo, int offset, + const void *hdr, u_int16_t datalen, + int *hotdrop) +{ + struct ipt_osf_info *info = (struct ipt_osf_info *)matchinfo; + struct iphdr *ip = skb->nh.iph; + struct tcphdr *tcp; + int fmatch = FMATCH_WRONG, fcount = 0; + unsigned long totlen, optsize = 0, window; + unsigned char df, *optp = NULL, *_optp = NULL; + char check_WSS = 0; + struct list_head *ent; + struct osf_finger *f; + + if (!ip || !info) + return 0; + + tcp = (struct tcphdr *)((u_int32_t *)ip + ip->ihl); + + if (!tcp->syn) + return 0; + + totlen = ntohs(ip->tot_len); + df = ((ntohs(ip->frag_off) & IP_DF)?1:0); + window = ntohs(tcp->window); + + if (tcp->doff*4 > sizeof(struct tcphdr)) + { + _optp = optp = (char *)(tcp+1); + optsize = tcp->doff*4 - sizeof(struct tcphdr); + } + + + /* Actually we can create hash/table of all genres and search + * only in appropriate part, but here is initial variant, + * so will use slow path. + */ + read_lock(&osf_lock); + list_for_each(ent, &finger_list) + { + f = list_entry(ent, struct osf_finger, flist); + + if (!(info->flags & IPT_OSF_LOG) && strcmp(info->genre, f->genre)) + continue; + + optp = _optp; + fmatch = FMATCH_WRONG; + + if (totlen == f->ss && df == f->df && + smart_dec(skb, info->flags, f->ttl)) + { + unsigned long foptsize; + int optnum; + unsigned short mss = 0; + + check_WSS = 0; + + switch (f->wss.wc) + { + case 0: check_WSS = 0; break; + case 'S': check_WSS = 1; break; + case 'T': check_WSS = 2; break; + case '%': check_WSS = 3; break; + default: log("Wrong fingerprint wss.wc=%d, %s - %s\n", + f->wss.wc, f->genre, f->details); + check_WSS = 4; + break; + } + if (check_WSS == 4) + continue; + + /* Check options */ + + foptsize = 0; + for (optnum=0; optnumopt_num; ++optnum) + foptsize += f->opt[optnum].length; + + + if (foptsize > MAX_IPOPTLEN || optsize > MAX_IPOPTLEN || optsize != foptsize) + continue; + + if (!optp) + { + fmatch = FMATCH_OK; + loga("\tYEP : matching without options.\n"); + if ((info->flags & IPT_OSF_LOG) && + info->loglevel == IPT_OSF_LOGLEVEL_FIRST) + break; + else + continue; + } + + + for (optnum=0; optnumopt_num; ++optnum) + { + if (f->opt[optnum].kind == (*optp)) + { + unsigned char len = f->opt[optnum].length; + unsigned char *optend = optp + len; + int loop_cont = 0; + + fmatch = FMATCH_OK; + + + switch (*optp) + { + case OSFOPT_MSS: + mss = ntohs(*(unsigned short *)(optp+2)); + break; + case OSFOPT_TS: + loop_cont = 1; + break; + } + + if (loop_cont) + { + optp = optend; + continue; + } + + if (len != 1) + { + /* Skip kind and length fields*/ + optp += 2; + + if (f->opt[optnum].wc.val != 0) + { + unsigned long tmp = 0; + + /* Hmmm... It looks a bit ugly. :) */ + memcpy(&tmp, optp, + (len > sizeof(unsigned long)? + sizeof(unsigned long):len)); + /* 2 + 2: optlen(2 bytes) + + * kind(1 byte) + length(1 byte) */ + if (len == 4) + tmp = ntohs(tmp); + else + tmp = ntohl(tmp); + + if (f->opt[optnum].wc.wc == '%') + { + if ((tmp % f->opt[optnum].wc.val) != 0) + fmatch = FMATCH_OPT_WRONG; + } + else if (tmp != f->opt[optnum].wc.val) + fmatch = FMATCH_OPT_WRONG; + } + } + + optp = optend; + } + else + fmatch = FMATCH_OPT_WRONG; + + if (fmatch != FMATCH_OK) + break; + } + + if (fmatch != FMATCH_OPT_WRONG) + { + fmatch = FMATCH_WRONG; + + switch (check_WSS) + { + case 0: + if (f->wss.val == 0 || window == f->wss.val) + fmatch = FMATCH_OK; + break; + case 1: /* MSS */ +/* Lurked in OpenBSD */ +#define SMART_MSS 1460 + if (window == f->wss.val*mss || + window == f->wss.val*SMART_MSS) + fmatch = FMATCH_OK; + break; + case 2: /* MTU */ + if (window == f->wss.val*(mss+40) || + window == f->wss.val*(SMART_MSS+40)) + fmatch = FMATCH_OK; + break; + case 3: /* MOD */ + if ((window % f->wss.val) == 0) + fmatch = FMATCH_OK; + break; + } + } + + + if (fmatch == FMATCH_OK) + { + fcount++; + log("%s [%s:%s:%s] : %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n", + f->genre, f->version, + f->subtype, f->details, + NIPQUAD(ip->saddr), ntohs(tcp->source), + NIPQUAD(ip->daddr), ntohs(tcp->dest), + f->ttl - ip->ttl); + if (info->flags & IPT_OSF_NETLINK) + { + spin_lock_bh(&ipt_osf_netlink_lock); + ipt_osf_nlsend(f, skb); + spin_unlock_bh(&ipt_osf_netlink_lock); + } + if ((info->flags & IPT_OSF_LOG) && + info->loglevel == IPT_OSF_LOGLEVEL_FIRST) + break; + } + } + } + if (!fcount && (info->flags & (IPT_OSF_LOG | IPT_OSF_NETLINK))) + { + unsigned char opt[4 * 15 - sizeof(struct tcphdr)]; + unsigned int i, optsize; + struct osf_finger fg; + + memset(&fg, 0, sizeof(fg)); + + if ((info->flags & IPT_OSF_LOG)) + log("Unknown: %lu:%d:%d:%lu:", window, ip->ttl, df, totlen); + if (optp) + { + optsize = tcp->doff * 4 - sizeof(struct tcphdr); + if (skb_copy_bits(skb, ip->ihl*4 + sizeof(struct tcphdr), + opt, optsize) < 0) + { + if (info->flags & IPT_OSF_LOG) + loga("TRUNCATED"); + if (info->flags & IPT_OSF_NETLINK) + strcpy(fg.details, "TRUNCATED"); + } + else + { + for (i = 0; i < optsize; i++) + { + if (info->flags & IPT_OSF_LOG) + loga("%02X", opt[i]); + } + if (info->flags & IPT_OSF_NETLINK) + memcpy(fg.details, opt, MAXDETLEN); + } + } + if ((info->flags & IPT_OSF_LOG)) + loga(" %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n", + NIPQUAD(ip->saddr), ntohs(tcp->source), + NIPQUAD(ip->daddr), ntohs(tcp->dest)); + + if (info->flags & IPT_OSF_NETLINK) + { + fg.wss.val = window; + fg.ttl = ip->ttl; + fg.df = df; + fg.ss = totlen; + strncpy(fg.genre, "Unknown", MAXGENRELEN); + + spin_lock_bh(&ipt_osf_netlink_lock); + ipt_osf_nlsend(&fg, skb); + spin_unlock_bh(&ipt_osf_netlink_lock); + } + } + + read_unlock(&osf_lock); + + return (fmatch == FMATCH_OK)?1:0; +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_osf_info))) + return 0; + if (ip->proto != IPPROTO_TCP) + return 0; + + return 1; +} + +static char * osf_strchr(char *ptr, char c) +{ + char *tmp; + + tmp = strchr(ptr, c); + + while (tmp && tmp+1 && isspace(*(tmp+1))) + tmp++; + + return tmp; +} + +static struct osf_finger * finger_alloc(void) +{ + struct osf_finger *f; + + f = kmalloc(sizeof(struct osf_finger), GFP_KERNEL); + if (f) + memset(f, 0, sizeof(struct osf_finger)); + + return f; +} + +static void finger_free(struct osf_finger *f) +{ + memset(f, 0, sizeof(struct osf_finger)); + kfree(f); +} + + +static void osf_parse_opt(struct osf_opt *opt, int *optnum, char *obuf, int olen) +{ + int i, op; + char *ptr, wc; + unsigned long val; + + ptr = &obuf[0]; + i = 0; + while (ptr != NULL && i < olen) + { + val = 0; + op = 0; + wc = 0; + switch (obuf[i]) + { + case 'N': + op = OSFOPT_NOP; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + *ptr = '\0'; + ptr++; + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + case 'S': + op = OSFOPT_SACKP; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + *ptr = '\0'; + ptr++; + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + case 'T': + op = OSFOPT_TS; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + *ptr = '\0'; + ptr++; + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + case 'W': + op = OSFOPT_WSO; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + switch (obuf[i+1]) + { + case '%': wc = '%'; break; + case 'S': wc = 'S'; break; + case 'T': wc = 'T'; break; + default: wc = 0; break; + } + + *ptr = '\0'; + ptr++; + if (wc) + val = simple_strtoul(&obuf[i+2], NULL, 10); + else + val = simple_strtoul(&obuf[i+1], NULL, 10); + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + case 'M': + op = OSFOPT_MSS; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + if (obuf[i+1] == '%') + wc = '%'; + *ptr = '\0'; + ptr++; + if (wc) + val = simple_strtoul(&obuf[i+2], NULL, 10); + else + val = simple_strtoul(&obuf[i+1], NULL, 10); + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + case 'E': + op = OSFOPT_EOL; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + *ptr = '\0'; + ptr++; + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + default: + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + ptr++; + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + } + + opt[*optnum].kind = IANA_opts[op].kind; + opt[*optnum].length = IANA_opts[op].length; + opt[*optnum].wc.wc = wc; + opt[*optnum].wc.val = val; + + (*optnum)++; + } +} + +static int osf_proc_read(char *buf, char **start, off_t off, int count, int *eof, void *data) +{ + struct list_head *ent; + struct osf_finger *f = NULL; + int i; + + *eof = 1; + count = 0; + + read_lock_bh(&osf_lock); + list_for_each(ent, &finger_list) + { + f = list_entry(ent, struct osf_finger, flist); + + log("%s [%s]", f->genre, f->details); + + count += sprintf(buf+count, "%s - %s[%s] : %s", + f->genre, f->version, + f->subtype, f->details); + + if (f->opt_num) + { + loga(" OPT: "); + //count += sprintf(buf+count, " OPT: "); + for (i=0; iopt_num; ++i) + { + //count += sprintf(buf+count, "%d.%c%lu; ", + // f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', f->opt[i].wc.val); + loga("%d.%c%lu; ", + f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', f->opt[i].wc.val); + } + } + loga("\n"); + count += sprintf(buf+count, "\n"); + } + read_unlock_bh(&osf_lock); + + return count; +} + +static int osf_proc_write(struct file *file, const char *buffer, unsigned long count, void *data) +{ + int cnt; + unsigned long i; + char obuf[MAXOPTSTRLEN]; + struct osf_finger *finger; + struct list_head *ent, *n; + + char *pbeg, *pend; + + if (count == strlen(OSFFLUSH) && !strncmp(buffer, OSFFLUSH, strlen(OSFFLUSH))) + { + int i = 0; + write_lock_bh(&osf_lock); + list_for_each_safe(ent, n, &finger_list) + { + i++; + finger = list_entry(ent, struct osf_finger, flist); + list_del(&finger->flist); + finger_free(finger); + } + write_unlock_bh(&osf_lock); + + log("Flushed %d entries.\n", i); + + return count; + } + + + cnt = 0; + for (i=0; iwss.wc = 'S'; + if (pbeg[1] == '%') + finger->wss.val = simple_strtoul(pbeg+2, NULL, 10); + else if (pbeg[1] == '*') + finger->wss.val = 0; + else + finger->wss.val = simple_strtoul(pbeg+1, NULL, 10); + } + else if (pbeg[0] == 'T') + { + finger->wss.wc = 'T'; + if (pbeg[1] == '%') + finger->wss.val = simple_strtoul(pbeg+2, NULL, 10); + else if (pbeg[1] == '*') + finger->wss.val = 0; + else + finger->wss.val = simple_strtoul(pbeg+1, NULL, 10); + } + else if (pbeg[0] == '%') + { + finger->wss.wc = '%'; + finger->wss.val = simple_strtoul(pbeg+1, NULL, 10); + } + else if (isdigit(pbeg[0])) + { + finger->wss.wc = 0; + finger->wss.val = simple_strtoul(pbeg, NULL, 10); + } + + pbeg = pend+1; + } + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + finger->ttl = simple_strtoul(pbeg, NULL, 10); + pbeg = pend+1; + } + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + finger->df = simple_strtoul(pbeg, NULL, 10); + pbeg = pend+1; + } + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + finger->ss = simple_strtoul(pbeg, NULL, 10); + pbeg = pend+1; + } + + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + cnt = snprintf(obuf, sizeof(obuf), "%s", pbeg); + pbeg = pend+1; + } + + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + if (pbeg[0] == '@' || pbeg[0] == '*') + cnt = snprintf(finger->genre, sizeof(finger->genre), "%s", pbeg+1); + else + cnt = snprintf(finger->genre, sizeof(finger->genre), "%s", pbeg); + pbeg = pend+1; + } + + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + cnt = snprintf(finger->version, sizeof(finger->version), "%s", pbeg); + pbeg = pend+1; + } + + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + cnt = snprintf(finger->subtype, sizeof(finger->subtype), "%s", pbeg); + pbeg = pend+1; + } + + cnt = snprintf(finger->details, + ((count - (pbeg - buffer)+1) > MAXDETLEN)?MAXDETLEN:(count - (pbeg - buffer)+1), + "%s", pbeg); + + log("%s - %s[%s] : %s\n", + finger->genre, finger->version, + finger->subtype, finger->details); + + osf_parse_opt(finger->opt, &finger->opt_num, obuf, sizeof(obuf)); + + + write_lock_bh(&osf_lock); + list_add_tail(&finger->flist, &finger_list); + write_unlock_bh(&osf_lock); + + return count; +} + +static int __init osf_init(void) +{ + int err; + struct proc_dir_entry *p; + + log("Startng OS fingerprint matching module.\n"); + + INIT_LIST_HEAD(&finger_list); + + err = ipt_register_match(&osf_match); + if (err) + { + log("Failed to register OS fingerprint matching module.\n"); + return -ENXIO; + } + + p = create_proc_entry("sys/net/ipv4/osf", S_IFREG | 0644, NULL); + if (!p) + { + ipt_unregister_match(&osf_match); + return -ENXIO; + } + + p->write_proc = osf_proc_write; + p->read_proc = osf_proc_read; + + nts = netlink_kernel_create(NETLINK_NFLOG, NULL); + if (!nts) + { + log("netlink_kernel_create() failed\n"); + remove_proc_entry("sys/net/ipv4/osf", NULL); + ipt_unregister_match(&osf_match); + return -ENOMEM; + } + + return 0; +} + +static void __exit osf_fini(void) +{ + struct list_head *ent, *n; + struct osf_finger *f; + + remove_proc_entry("sys/net/ipv4/osf", NULL); + ipt_unregister_match(&osf_match); + if (nts && nts->socket) + sock_release(nts->socket); + + list_for_each_safe(ent, n, &finger_list) + { + f = list_entry(ent, struct osf_finger, flist); + list_del(&f->flist); + finger_free(f); + } + + log("OS fingerprint matching module finished.\n"); +} + +module_init(osf_init); +module_exit(osf_fini); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Evgeniy Polyakov "); +MODULE_DESCRIPTION("Passive OS fingerprint matching."); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_owner.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_owner.c --- linux-2.4.26/net/ipv4/netfilter/ipt_owner.c Tue Jan 7 15:50:49 2003 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_owner.c Sun Apr 18 17:33:08 2004 @@ -2,17 +2,25 @@ locally generated outgoing packets. Copyright (C) 2000 Marc Boucher + + 03/26/2003 Patrick McHardy : LOCAL_IN support */ #include #include #include +#include +#include +#include #include +#include +#include +#include #include #include static int -match_comm(const struct sk_buff *skb, const char *comm) +match_comm(const struct sock *sk, const char *comm) { struct task_struct *p; struct files_struct *files; @@ -28,7 +36,7 @@ if(files) { read_lock(&files->file_lock); for (i=0; i < files->max_fds; i++) { - if (fcheck_files(files, i) == skb->sk->socket->file) { + if (fcheck_files(files, i) == sk->socket->file) { read_unlock(&files->file_lock); task_unlock(p); read_unlock(&tasklist_lock); @@ -44,7 +52,7 @@ } static int -match_pid(const struct sk_buff *skb, pid_t pid) +match_pid(const struct sock *sk, pid_t pid) { struct task_struct *p; struct files_struct *files; @@ -59,7 +67,7 @@ if(files) { read_lock(&files->file_lock); for (i=0; i < files->max_fds; i++) { - if (fcheck_files(files, i) == skb->sk->socket->file) { + if (fcheck_files(files, i) == sk->socket->file) { read_unlock(&files->file_lock); task_unlock(p); read_unlock(&tasklist_lock); @@ -75,10 +83,10 @@ } static int -match_sid(const struct sk_buff *skb, pid_t sid) +match_sid(const struct sock *sk, pid_t sid) { struct task_struct *p; - struct file *file = skb->sk->socket->file; + struct file *file = sk->socket->file; int i, found=0; read_lock(&tasklist_lock); @@ -119,41 +127,71 @@ int *hotdrop) { const struct ipt_owner_info *info = matchinfo; - - if (!skb->sk || !skb->sk->socket || !skb->sk->socket->file) - return 0; + struct iphdr *iph = skb->nh.iph; + struct sock *sk = NULL; + int ret = 0; + + if (out) { + sk = skb->sk; + } else { + if (iph->protocol == IPPROTO_TCP) { + struct tcphdr *tcph = + (struct tcphdr*)((u_int32_t*)iph + iph->ihl); + sk = tcp_v4_lookup(iph->saddr, tcph->source, + iph->daddr, tcph->dest, + ((struct rtable*)skb->dst)->rt_iif); + if (sk && sk->state == TCP_TIME_WAIT) { + tcp_tw_put((struct tcp_tw_bucket *)sk); + return ret; + } + } else if (iph->protocol == IPPROTO_UDP) { + struct udphdr *udph = + (struct udphdr*)((u_int32_t*)iph + iph->ihl); + sk = udp_v4_lookup(iph->saddr, udph->source, iph->daddr, + udph->dest, skb->dev->ifindex); + } + } + + if (!sk || !sk->socket || !sk->socket->file) + goto out; if(info->match & IPT_OWNER_UID) { - if((skb->sk->socket->file->f_uid != info->uid) ^ + if((sk->socket->file->f_uid != info->uid) ^ !!(info->invert & IPT_OWNER_UID)) - return 0; + goto out; } if(info->match & IPT_OWNER_GID) { - if((skb->sk->socket->file->f_gid != info->gid) ^ + if((sk->socket->file->f_gid != info->gid) ^ !!(info->invert & IPT_OWNER_GID)) - return 0; + goto out; } if(info->match & IPT_OWNER_PID) { - if (!match_pid(skb, info->pid) ^ + if (!match_pid(sk, info->pid) ^ !!(info->invert & IPT_OWNER_PID)) - return 0; + goto out; } if(info->match & IPT_OWNER_SID) { - if (!match_sid(skb, info->sid) ^ + if (!match_sid(sk, info->sid) ^ !!(info->invert & IPT_OWNER_SID)) - return 0; + goto out; } if(info->match & IPT_OWNER_COMM) { - if (!match_comm(skb, info->comm) ^ + if (!match_comm(sk, info->comm) ^ !!(info->invert & IPT_OWNER_COMM)) - return 0; + goto out; } - return 1; + ret = 1; + +out: + if (in && sk) + sock_put(sk); + + return ret; } static int @@ -164,10 +202,18 @@ unsigned int hook_mask) { if (hook_mask - & ~((1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_POST_ROUTING))) { - printk("ipt_owner: only valid for LOCAL_OUT or POST_ROUTING.\n"); + & ~((1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_POST_ROUTING) | + (1 << NF_IP_LOCAL_IN))) { + printk("ipt_owner: only valid for LOCAL_IN, LOCAL_OUT " + "or POST_ROUTING.\n"); return 0; } + + if ((hook_mask & (1 << NF_IP_LOCAL_IN)) + && ip->proto != IPPROTO_TCP && ip->proto != IPPROTO_UDP) { + printk("ipt_owner: only TCP or UDP can be used in LOCAL_IN\n"); + return 0; + } if (matchsize != IPT_ALIGN(sizeof(struct ipt_owner_info))) return 0; diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_pool.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_pool.c --- linux-2.4.26/net/ipv4/netfilter/ipt_pool.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_pool.c Sun Apr 18 17:33:08 2004 @@ -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 -urN linux-2.4.26/net/ipv4/netfilter/ipt_psd.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_psd.c --- linux-2.4.26/net/ipv4/netfilter/ipt_psd.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_psd.c Sun Apr 18 17:33:08 2004 @@ -0,0 +1,361 @@ +/* + 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"); +MODULE_AUTHOR("Dennis Koslowski "); + +#define HF_DADDR_CHANGING 0x01 +#define HF_SPORT_CHANGING 0x02 +#define HF_TOS_CHANGING 0x04 +#define HF_TTL_CHANGING 0x08 + +/* + * Information we keep per each target port + */ +struct port { + u_int16_t number; /* port number */ + u_int8_t proto; /* protocol number */ + u_int8_t and_flags; /* tcp ANDed flags */ + u_int8_t or_flags; /* tcp ORed flags */ +}; + +/* + * Information we keep per each source address. + */ +struct host { + struct host *next; /* Next entry with the same hash */ + clock_t timestamp; /* Last update time */ + struct in_addr src_addr; /* Source address */ + struct in_addr dest_addr; /* Destination address */ + unsigned short src_port; /* Source port */ + int count; /* Number of ports in the list */ + int weight; /* Total weight of ports in the list */ + struct port ports[SCAN_MAX_COUNT - 1]; /* List of ports */ + unsigned char tos; /* TOS */ + unsigned char ttl; /* TTL */ + unsigned char flags; /* HF_ flags bitmask */ +}; + +/* + * State information. + */ +static struct { + spinlock_t lock; + struct host list[LIST_SIZE]; /* List of source addresses */ + struct host *hash[HASH_SIZE]; /* Hash: pointers into the list */ + int index; /* Oldest entry to be replaced */ +} state; + +/* + * Convert an IP address into a hash table index. + */ +static inline int hashfunc(struct in_addr addr) +{ + unsigned int value; + int hash; + + value = addr.s_addr; + hash = 0; + do { + hash ^= value; + } while ((value >>= HASH_LOG)); + + return hash & (HASH_SIZE - 1); +} + +static int +ipt_psd_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + 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 -urN linux-2.4.26/net/ipv4/netfilter/ipt_random.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_random.c --- linux-2.4.26/net/ipv4/netfilter/ipt_random.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_random.c Sun Apr 18 17:33:09 2004 @@ -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 -urN linux-2.4.26/net/ipv4/netfilter/ipt_rpc.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_rpc.c --- linux-2.4.26/net/ipv4/netfilter/ipt_rpc.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_rpc.c Sun Apr 18 17:33:09 2004 @@ -0,0 +1,428 @@ +/* RPC extension for IP connection matching, Version 2.2 + * (C) 2000 by Marcelo Barbosa Lima + * - original rpc tracking module + * - "recent" connection handling for kernel 2.3+ netfilter + * + * (C) 2001 by Rusty Russell + * - upgraded conntrack modules to oldnat api - kernel 2.4.0+ + * + * (C) 2002,2003 by Ian (Larry) Latter + * - upgraded conntrack modules to newnat api - kernel 2.4.20+ + * - extended matching to support filtering on procedures + * + * ipt_rpc.c,v 2.2 2003/01/12 18:30:00 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + ** + * Module load syntax: + * insmod ipt_rpc.o ports=port1,port2,...port + * + * Please give the ports of all RPC servers you wish to connect to. + * If you don't specify ports, the default will be port 111. + ** + * Note to all: + * + * RPCs should not be exposed to the internet - ask the Pentagon; + * + * "The unidentified crackers pleaded guilty in July to charges + * of juvenile delinquency stemming from a string of Pentagon + * network intrusions in February. + * + * The youths, going by the names TooShort and Makaveli, used + * a common server security hole to break in, according to + * Dane Jasper, owner of the California Internet service + * provider, Sonic. They used the hole, known as the 'statd' + * exploit, to attempt more than 800 break-ins, Jasper said." + * + * From: Wired News; "Pentagon Kids Kicked Off Grid" - Nov 6, 1998 + * URL: http://www.wired.com/news/politics/0,1283,16098,00.html + ** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_n_c = 0; + +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers (TCP/UDP) of RPC portmapper servers"); +#endif + +MODULE_AUTHOR("Marcelo Barbosa Lima "); +MODULE_DESCRIPTION("RPC connection matching module"); +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP(format, args...) printk(KERN_DEBUG "ipt_rpc: " \ + format, ## args) +#else +#define DEBUGP(format, args...) +#endif + +EXPORT_NO_SYMBOLS; + +/* vars from ip_conntrack_rpc_tcp */ +extern struct list_head request_p_list_tcp; +extern struct module *ip_conntrack_rpc_tcp; + +/* vars from ip_conntrack_rpc_udp */ +extern struct list_head request_p_list_udp; +extern struct module *ip_conntrack_rpc_udp; + +DECLARE_RWLOCK_EXTERN(ipct_rpc_tcp_lock); +DECLARE_RWLOCK_EXTERN(ipct_rpc_udp_lock); + +#define ASSERT_READ_LOCK(x) \ +do { \ + if (x == &request_p_list_udp) \ + MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock); \ + else if (x == &request_p_list_tcp) \ + MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock); \ +} while (0) + +#define ASSERT_WRITE_LOCK(x) \ +do { \ + if (x == &request_p_list_udp) \ + MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock); \ + else if (x == &request_p_list_tcp) \ + MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock); \ +} while (0) + +#include + +const int IPT_RPC_CHAR_LEN = 11; + + +static int k_atoi(char *string) +{ + unsigned int result = 0; + int maxoctet = IPT_RPC_CHAR_LEN; + + for ( ; *string != 0 && maxoctet != 0; maxoctet--, string++) { + if (*string < 0) + return(0); + if (*string == 0) + break; + if (*string < 48 || *string > 57) { + return(0); + } + result = result * 10 + ( *string - 48 ); + } + return(result); +} + + +static int match_rpcs(char *c_procs, int i_procs, int proc) +{ + int proc_ctr; + char *proc_ptr; + unsigned int proc_num; + + DEBUGP("entered match_rpcs [%i] [%i] ..\n", i_procs, proc); + + if (i_procs == -1) + return 1; + + for (proc_ctr=0; proc_ctr <= i_procs; proc_ctr++) { + + proc_ptr = c_procs; + proc_ptr += proc_ctr * IPT_RPC_CHAR_LEN; + proc_num = k_atoi(proc_ptr); + + if (proc_num == proc) + return 1; + } + + return 0; +} + + +static int check_rpc_packet(const u_int32_t *data, const void *matchinfo, + int *hotdrop, int dir, struct ip_conntrack *ct, + int offset, struct list_head request_p_list) +{ + const struct ipt_rpc_info *rpcinfo = matchinfo; + struct request_p *req_p; + u_int32_t xid; + + + /* Get XID */ + xid = *data; + + /* This does sanity checking on RPC payloads, + * and permits only the RPC "get port" (3) + * in authorised procedures in client + * communications with the portmapper. + */ + + data += 5; + + /* Get RPC requestor */ + if (IXDR_GET_INT32(data) != 3) { + DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n"); + if(rpcinfo->strict == 1) + *hotdrop = 1; + return 0; + } + DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n"); + + data++; + + /* Jump Credentials and Verfifier */ + data = data + IXDR_GET_INT32(data) + 2; + data = data + IXDR_GET_INT32(data) + 2; + + /* Get RPC procedure */ + if (match_rpcs((char *)&rpcinfo->c_procs, + rpcinfo->i_procs, IXDR_GET_INT32(data)) == 0) { + DEBUGP("RPC packet contains illegal procedure request [%u]. [drop]\n", + (unsigned int)IXDR_GET_INT32(data)); + + /* If the RPC conntrack half entry already exists .. */ + + switch (ct->tuplehash[0].tuple.dst.protonum) { + case IPPROTO_UDP: + WRITE_LOCK(&ipct_rpc_udp_lock); + case IPPROTO_TCP: + WRITE_LOCK(&ipct_rpc_tcp_lock); + } + req_p = LIST_FIND(&request_p_list, request_p_cmp, + struct request_p *, xid, + ct->tuplehash[dir].tuple.src.ip, + ct->tuplehash[dir].tuple.src.u.all); + + if (req_p) { + DEBUGP("found req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n", + xid, ct->tuplehash[dir].tuple.dst.protonum, + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + ntohs(ct->tuplehash[dir].tuple.src.u.all)); + + /* .. remove it */ + if (del_timer(&req_p->timeout)) + req_p->timeout.expires = 0; + + LIST_DELETE(&request_p_list, req_p); + DEBUGP("RPC req_p removed. [done]\n"); + + } else { + DEBUGP("no req_p found for xid=%u proto=%u %u.%u.%u.%u:%u\n", + xid, ct->tuplehash[dir].tuple.dst.protonum, + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + ntohs(ct->tuplehash[dir].tuple.src.u.all)); + + } + switch (ct->tuplehash[0].tuple.dst.protonum) { + case IPPROTO_UDP: + WRITE_UNLOCK(&ipct_rpc_udp_lock); + case IPPROTO_TCP: + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + } + + if(rpcinfo->strict == 1) + *hotdrop = 1; + return 0; + } + + DEBUGP("RPC packet contains authorised procedure request [%u]. [match]\n", + (unsigned int)IXDR_GET_INT32(data)); + return (1 && (!offset)); +} + + +static int match(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const void *matchinfo, + int offset, const void *hdr, u_int16_t datalen, int *hotdrop) +{ + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + const u_int32_t *data; + enum ip_conntrack_dir dir; + const struct tcphdr *tcp; + const struct ipt_rpc_info *rpcinfo = matchinfo; + int port, portsok; + int tval; + + + DEBUGP("new packet to evaluate ..\n"); + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + if (!ct) { + DEBUGP("no ct available [skip]\n"); + return 0; + } + + DEBUGP("ct detected. [cont]\n"); + dir = CTINFO2DIR(ctinfo); + + /* we only want the client to server packets for matching */ + if (dir != IP_CT_DIR_ORIGINAL) + return 0; + + /* This does sanity checking on UDP or TCP packets, + * like their respective modules. + */ + + switch (ct->tuplehash[0].tuple.dst.protonum) { + + case IPPROTO_UDP: + DEBUGP("PROTO_UDP [cont]\n"); + if (offset == 0 && datalen < sizeof(struct udphdr)) { + DEBUGP("packet does not contain a complete header. [drop]\n"); + return 0; + } + + for (port=0,portsok=0; port <= ports_n_c; port++) { + if (ntohs(ct->tuplehash[dir].tuple.dst.u.all) == ports[port]) { + portsok++; + break; + } + } + if (portsok == 0) { + DEBUGP("packet is not destined for a portmapper [%u]. [skip]\n", + ntohs(ct->tuplehash[dir].tuple.dst.u.all)); + return 0; + } + + if ((datalen - sizeof(struct udphdr)) != 56) { + DEBUGP("packet length is not correct for RPC content. [skip]\n"); + if (rpcinfo->strict == 1) + *hotdrop = 1; + return 0; + } + DEBUGP("packet length is correct. [cont]\n"); + + /* Get to the data */ + data = (const u_int32_t *)hdr + 2; + + /* Check the RPC data */ + tval = check_rpc_packet(data, matchinfo, hotdrop, + dir, ct, offset, + request_p_list_udp); + + return tval; + + + case IPPROTO_TCP: + DEBUGP("PROTO_TCP [cont]\n"); + if (offset == 0 && datalen < sizeof(struct tcphdr)) { + DEBUGP("packet does not contain a complete header. [drop]\n"); + return 0; + } + + for (port=0,portsok=0; port <= ports_n_c; port++) { + if (ntohs(ct->tuplehash[dir].tuple.dst.u.all) == ports[port]) { + portsok++; + break; + } + } + if (portsok == 0) { + DEBUGP("packet is not destined for a portmapper [%u]. [skip]\n", + ntohs(ct->tuplehash[dir].tuple.dst.u.all)); + return 0; + } + + tcp = hdr; + if (datalen == (tcp->doff * 4)) { + DEBUGP("packet does not contain any data. [match]\n"); + return (1 && (!offset)); + } + + /* Tests if packet len is ok */ + if ((datalen - (tcp->doff * 4)) != 60) { + DEBUGP("packet length is not correct for RPC content. [skip]\n"); + if(rpcinfo->strict == 1) + *hotdrop = 1; + return 0; + } + DEBUGP("packet length is correct. [cont]\n"); + + /* Get to the data */ + data = (const u_int32_t *)tcp + tcp->doff + 1; + + /* Check the RPC data */ + tval = check_rpc_packet(data, matchinfo, hotdrop, + dir, ct, offset, + request_p_list_tcp); + + return tval; + + } + + DEBUGP("transport protocol=%u, is not supported [skip]\n", + ct->tuplehash[0].tuple.dst.protonum); + return 0; +} + + +static int checkentry(const char *tablename, const struct ipt_ip *ip, void *matchinfo, + unsigned int matchsize, unsigned int hook_mask) +{ + if (hook_mask + & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_FORWARD) | (1 << NF_IP_POST_ROUTING) + | (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_LOCAL_OUT))) { + printk("ipt_rpc: only valid for PRE_ROUTING, FORWARD, POST_ROUTING, LOCAL_IN and/or LOCAL_OUT targets.\n"); + return 0; + } + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_rpc_info))) + return 0; + + return 1; +} + + +static struct ipt_match rpc_match = { { NULL, NULL }, "rpc", + &match, &checkentry, NULL, + THIS_MODULE }; + + +static int __init init(void) +{ + int port; + + DEBUGP("incrementing usage counts\n"); + __MOD_INC_USE_COUNT(ip_conntrack_rpc_udp); + __MOD_INC_USE_COUNT(ip_conntrack_rpc_tcp); + + /* If no port given, default to standard RPC port */ + if (ports[0] == 0) + ports[0] = RPC_PORT; + + DEBUGP("registering match [%s] for;\n", rpc_match.name); + for (port = 0; (port < MAX_PORTS) && ports[port]; port++) { + DEBUGP(" port %i (UDP|TCP);\n", ports[port]); + ports_n_c++; + } + + return ipt_register_match(&rpc_match); +} + + +static void fini(void) +{ + DEBUGP("unregistering match\n"); + ipt_unregister_match(&rpc_match); + + DEBUGP("decrementing usage counts\n"); + __MOD_DEC_USE_COUNT(ip_conntrack_rpc_tcp); + __MOD_DEC_USE_COUNT(ip_conntrack_rpc_udp); +} + + +module_init(init); +module_exit(fini); + diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_set.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_set.c --- linux-2.4.26/net/ipv4/netfilter/ipt_set.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_set.c Sun Apr 18 17:33:10 2004 @@ -0,0 +1,105 @@ +/* Kernel module to match an IP address set. */ + +#include +#include +#include + +#include +#include +#include + +static inline int +match_set(const struct ipt_set_info *info, + const struct sk_buff *skb, + int inv) +{ + if (ip_set_testip_kernel(ip_set_list[info->id], + skb, + info->flags, + info->set_level, + info->ip_level)) + 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_set_info_match *info = matchinfo; + + if (info->match.id < 0) + return 0; + + return match_set(&info->match, + skb, + info->match.flags[0] & IPSET_MATCH_INV); +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_set_info_match *info = matchinfo; + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { + ip_set_printk("invalid matchsize %d", matchsize); + return 0; + } + + if (info->match.id < 0) + return 1; + + if (!ip_set_get_byid(info->match.id)) { + ip_set_printk("cannot verify setid %i to match", + info->match.id); + return 0; /* error */ + } + DP("checkentry OK"); + + return 1; +} + +static void destroy(void *matchinfo, unsigned int matchsize) +{ + struct ipt_set_info_match *info = matchinfo; + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { + ip_set_printk("invalid matchsize %d", matchsize); + return; + } + + if (info->match.id >= 0) + ip_set_put(ip_set_list[info->match.id]); +} + +static struct ipt_match set_match = { + .name = "set", + .match = &match, + .checkentry = &checkentry, + .destroy = &destroy, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_match(&set_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&set_match); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.26/net/ipv4/netfilter/ipt_string.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_string.c --- linux-2.4.26/net/ipv4/netfilter/ipt_string.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_string.c Sun Apr 18 17:33:10 2004 @@ -0,0 +1,218 @@ +/* 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 + +MODULE_LICENSE("GPL"); + +struct string_per_cpu { + int *skip; + int *shift; + int *len; +}; + +struct string_per_cpu *bm_string_data=NULL; + +/* Boyer Moore Sublinear string search - VERY FAST */ +char *search_sublinear (char *needle, char *haystack, int needle_len, int haystack_len) +{ + int M1, right_end, sk, sh; + int ended, j, i; + + int *skip, *shift, *len; + + /* use data suitable for this CPU */ + shift=bm_string_data[smp_processor_id()].shift; + skip=bm_string_data[smp_processor_id()].skip; + len=bm_string_data[smp_processor_id()].len; + + /* Setup skip/shift tables */ + M1 = right_end = needle_len-1; + for (i = 0; i < BM_MAX_HLEN; i++) skip[i] = needle_len; + for (i = 0; needle[i]; i++) skip[needle[i]] = M1 - i; + + for (i = 1; i < needle_len; i++) { + for (j = 0; j < needle_len && needle[M1 - j] == needle[M1 - i - j]; j++); + len[i] = j; + } + + shift[0] = 1; + for (i = 1; i < needle_len; i++) shift[i] = needle_len; + for (i = M1; i > 0; i--) shift[len[i]] = i; + ended = 0; + + for (i = 0; i < needle_len; i++) { + if (len[i] == M1 - i) ended = i; + if (ended) shift[i] = ended; + } + + /* Do the search*/ + while (right_end < haystack_len) + { + for (i = 0; i < needle_len && haystack[right_end - i] == needle[M1 - i]; i++); + if (i == needle_len) { + return haystack+(right_end - M1); + } + + sk = skip[haystack[right_end - i]]; + sh = shift[i]; + right_end = max(right_end - i + sk, right_end + sh); + } + + return NULL; +} + +/* Linear string search based on memcmp() */ +char *search_linear (char *needle, char *haystack, int needle_len, int haystack_len) +{ + char *k = haystack + (haystack_len-needle_len); + char *t = haystack; + + while ( t <= k ) { + if (memcmp(t, needle, needle_len) == 0) + return t; + t++; + } + + return NULL; +} + + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_string_info *info = matchinfo; + struct iphdr *ip = skb->nh.iph; + int hlen, nlen; + char *needle, *haystack; + proc_ipt_search search=search_linear; + + if ( !ip ) return 0; + + /* get lenghts, and validate them */ + nlen=info->len; + hlen=ntohs(ip->tot_len)-(ip->ihl*4); + if ( nlen > hlen ) return 0; + + needle=(char *)&info->string; + haystack=(char *)ip+(ip->ihl*4); + + /* The sublinear search comes in to its own + * on the larger packets */ + if ( (hlen>IPT_STRING_HAYSTACK_THRESH) && + (nlen>IPT_STRING_NEEDLE_THRESH) ) { + if ( hlen < BM_MAX_HLEN ) { + search=search_sublinear; + }else{ + if (net_ratelimit()) + printk(KERN_INFO "ipt_string: Packet too big " + "to attempt sublinear string search " + "(%d bytes)\n", hlen ); + } + } + + return ((search(needle, haystack, nlen, hlen)!=NULL) ^ info->invert); +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_string_info))) + return 0; + + return 1; +} + +void string_freeup_data(void) +{ + int c; + + if ( bm_string_data ) { + for(c=0; c : 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 -urN linux-2.4.26/net/ipv4/netfilter/ipt_u32.c linux-2.4.26-pomng/net/ipv4/netfilter/ipt_u32.c --- linux-2.4.26/net/ipv4/netfilter/ipt_u32.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv4/netfilter/ipt_u32.c Sun Apr 18 17:33:11 2004 @@ -0,0 +1,211 @@ +/* Kernel module to match u32 packet content. */ + +/* +U32 tests whether quantities of up to 4 bytes extracted from a packet +have specified values. The specification of what to extract is general +enough to find data at given offsets from tcp headers or payloads. + + --u32 tests + The argument amounts to a program in a small language described below. + tests := location = value | tests && location = value + value := range | value , range + range := number | number : number + a single number, n, is interpreted the same as n:n + n:m is interpreted as the range of numbers >=n and <=m + location := number | location operator number + operator := & | << | >> | @ + + The operators &, <<, >>, && mean the same as in c. The = is really a set + membership operator and the value syntax describes a set. The @ operator + is what allows moving to the next header and is described further below. + + *** Until I can find out how to avoid it, there are some artificial limits + on the size of the tests: + - no more than 10 ='s (and 9 &&'s) in the u32 argument + - no more than 10 ranges (and 9 commas) per value + - no more than 10 numbers (and 9 operators) per location + + To describe the meaning of location, imagine the following machine that + interprets it. There are three registers: + A is of type char*, initially the address of the IP header + B and C are unsigned 32 bit integers, initially zero + + The instructions are: + number B = number; + C = (*(A+B)<<24)+(*(A+B+1)<<16)+(*(A+B+2)<<8)+*(A+B+3) + &number C = C&number + <>number C = C>>number + @number A = A+C; then do the instruction number + Any access of memory outside [skb->head,skb->end] causes the match to fail. + Otherwise the result of the computation is the final value of C. + + Whitespace is allowed but not required in the tests. + However the characters that do occur there are likely to require + shell quoting, so it's a good idea to enclose the arguments in quotes. + +Example: + match IP packets with total length >= 256 + The IP header contains a total length field in bytes 2-3. + --u32 "0&0xFFFF=0x100:0xFFFF" + read bytes 0-3 + AND that with FFFF (giving bytes 2-3), + and test whether that's in the range [0x100:0xFFFF] + +Example: (more realistic, hence more complicated) + match icmp packets with icmp type 0 + First test that it's an icmp packet, true iff byte 9 (protocol) = 1 + --u32 "6&0xFF=1 && ... + read bytes 6-9, use & to throw away bytes 6-8 and compare the result to 1 + Next test that it's not a fragment. + (If so it might be part of such a packet but we can't always tell.) + n.b. This test is generally needed if you want to match anything + beyond the IP header. + The last 6 bits of byte 6 and all of byte 7 are 0 iff this is a complete + packet (not a fragment). Alternatively, you can allow first fragments + by only testing the last 5 bits of byte 6. + ... 4&0x3FFF=0 && ... + Last test: the first byte past the IP header (the type) is 0 + This is where we have to use the @syntax. The length of the IP header + (IHL) in 32 bit words is stored in the right half of byte 0 of the + IP header itself. + ... 0>>22&0x3C@0>>24=0" + The first 0 means read bytes 0-3, + >>22 means shift that 22 bits to the right. Shifting 24 bits would give + the first byte, so only 22 bits is four times that plus a few more bits. + &3C then eliminates the two extra bits on the right and the first four + bits of the first byte. + For instance, if IHL=5 then the IP header is 20 (4 x 5) bytes long. + In this case bytes 0-1 are (in binary) xxxx0101 yyzzzzzz, + >>22 gives the 10 bit value xxxx0101yy and &3C gives 010100. + @ means to use this number as a new offset into the packet, and read + four bytes starting from there. This is the first 4 bytes of the icmp + payload, of which byte 0 is the icmp type. Therefore we simply shift + the value 24 to the right to throw out all but the first byte and compare + the result with 0. + +Example: + tcp payload bytes 8-12 is any of 1, 2, 5 or 8 + First we test that the packet is a tcp packet (similar to icmp). + --u32 "6&0xFF=6 && ... + Next, test that it's not a fragment (same as above). + ... 0>>22&0x3C@12>>26&0x3C@8=1,2,5,8" + 0>>22&3C as above computes the number of bytes in the IP header. + @ makes this the new offset into the packet, which is the start of the + tcp header. The length of the tcp header (again in 32 bit words) is + the left half of byte 12 of the tcp header. The 12>>26&3C + computes this length in bytes (similar to the IP header before). + @ makes this the new offset, which is the start of the tcp payload. + Finally 8 reads bytes 8-12 of the payload and = checks whether the + result is any of 1, 2, 5 or 8 +*/ + +#include +#include + +#include +#include + +/* #include for timing */ + +MODULE_AUTHOR("Don Cohen "); +MODULE_DESCRIPTION("IP tables u32 matching module"); +MODULE_LICENSE("GPL"); + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_u32 *data = matchinfo; + int testind, i; + unsigned char* origbase = (char*)skb->nh.iph; + unsigned char* base = origbase; + unsigned char* head = skb->head; + unsigned char* end = skb->end; + int nnums, nvals; + u_int32_t pos, val; + /* unsigned long long cycles1, cycles2, cycles3, cycles4; + cycles1 = get_cycles(); */ + + for (testind=0; testind < data->ntests; testind++) { + base = origbase; /* reset for each test */ + pos = data->tests[testind].location[0].number; + if (base+pos+3 > end || base+pos < head) + return 0; + val = (base[pos]<<24) + (base[pos+1]<<16) + + (base[pos+2]<<8) + base[pos+3]; + nnums = data->tests[testind].nnums; + for (i=1; i < nnums; i++) { + u_int32_t number = data->tests[testind].location[i].number; + switch (data->tests[testind].location[i].nextop) { + case IPT_U32_AND: + val = val & number; + break; + case IPT_U32_LEFTSH: + val = val << number; + break; + case IPT_U32_RIGHTSH: + val = val >> number; + break; + case IPT_U32_AT: + base = base + val; + pos = number; + if (base+pos+3 > end || base+pos < head) + return 0; + val = (base[pos]<<24) + (base[pos+1]<<16) + + (base[pos+2]<<8) + base[pos+3]; + break; + } + } + nvals = data->tests[testind].nvalues; + for (i=0; i < nvals; i++) { + if ((data->tests[testind].value[i].min <= val) && + (val <= data->tests[testind].value[i].max)) { + break; + } + } + if (i >= data->tests[testind].nvalues) { + /* cycles2 = get_cycles(); + printk("failed %d in %d cycles\n", testind, + cycles2-cycles1); */ + return 0; + } + } + /* cycles2 = get_cycles(); + printk("succeeded in %d cycles\n", cycles2-cycles1); */ + return 1; +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_u32))) + return 0; + return 1; +} + +static struct ipt_match u32_match += { { NULL, NULL }, "u32", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&u32_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&u32_match); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv6/netfilter/Config.in linux-2.4.26-pomng/net/ipv6/netfilter/Config.in --- linux-2.4.26/net/ipv6/netfilter/Config.in Tue Apr 22 03:05:12 2003 +++ linux-2.4.26-pomng/net/ipv6/netfilter/Config.in Sun Apr 18 17:33:09 2004 @@ -17,7 +17,10 @@ if [ "$CONFIG_IP6_NF_IPTABLES" != "n" ]; then # The simple matches. dep_tristate ' limit match support' CONFIG_IP6_NF_MATCH_LIMIT $CONFIG_IP6_NF_IPTABLES + dep_tristate ' condition match support' CONFIG_IP6_NF_MATCH_CONDITION $CONFIG_IP6_NF_IPTABLES dep_tristate ' MAC address match support' CONFIG_IP6_NF_MATCH_MAC $CONFIG_IP6_NF_IPTABLES + dep_tristate ' Random match support' CONFIG_IP6_NF_MATCH_RANDOM $CONFIG_IP6_NF_IPTABLES + dep_tristate ' Nth match support' CONFIG_IP6_NF_MATCH_NTH $CONFIG_IP6_NF_IPTABLES if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' Routing header match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_RT $CONFIG_IP6_NF_IPTABLES fi @@ -57,6 +60,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 @@ -71,7 +77,7 @@ if [ "$CONFIG_IP6_NF_MANGLE" != "n" ]; then # dep_tristate ' TOS target support' CONFIG_IP6_NF_TARGET_TOS $CONFIG_IP_NF_MANGLE dep_tristate ' MARK target support' CONFIG_IP6_NF_TARGET_MARK $CONFIG_IP6_NF_MANGLE - fi + dep_mbool ' ROUTE target support' CONFIG_IP6_NF_TARGET_ROUTE $CONFIG_IP6_NF_MANGLE fi #dep_tristate ' LOG target support' CONFIG_IP6_NF_TARGET_LOG $CONFIG_IP6_NF_IPTABLES fi diff -urN linux-2.4.26/net/ipv6/netfilter/Makefile linux-2.4.26-pomng/net/ipv6/netfilter/Makefile --- linux-2.4.26/net/ipv6/netfilter/Makefile Tue Apr 22 03:05:12 2003 +++ linux-2.4.26-pomng/net/ipv6/netfilter/Makefile Sun Apr 18 17:33:09 2004 @@ -14,6 +14,7 @@ # Link order matters here. obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o obj-$(CONFIG_IP6_NF_MATCH_LIMIT) += ip6t_limit.o +obj-$(CONFIG_IP6_NF_MATCH_CONDITION) += ip6t_condition.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 @@ -28,8 +29,14 @@ obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o obj-$(CONFIG_IP6_NF_TARGET_MARK) += ip6t_MARK.o +obj-$(CONFIG_IP6_NF_TARGET_ROUTE) += ip6t_ROUTE.o +obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o + +obj-$(CONFIG_IP6_NF_MATCH_RANDOM) += ip6t_random.o + +obj-$(CONFIG_IP6_NF_MATCH_NTH) += ip6t_nth.o obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o include $(TOPDIR)/Rules.make diff -urN linux-2.4.26/net/ipv6/netfilter/ip6t_LOG.c linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_LOG.c --- linux-2.4.26/net/ipv6/netfilter/ip6t_LOG.c Wed Jul 30 09:19:15 2003 +++ linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_LOG.c Sun Apr 18 17:33:07 2004 @@ -10,6 +10,7 @@ #include #include #include +#include MODULE_AUTHOR("Jan Rekorajski "); MODULE_DESCRIPTION("IP6 tables LOG target module"); @@ -266,23 +267,21 @@ } } -static unsigned int -ip6t_log_target(struct sk_buff **pskb, +static void +ip6t_log_packet(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, const struct net_device *out, - const void *targinfo, - void *userinfo) + const struct ip6t_log_info *loginfo, + const char *level_string, + const char *prefix) { struct ipv6hdr *ipv6h = (*pskb)->nh.ipv6h; - const struct ip6t_log_info *loginfo = targinfo; - char level_string[4] = "< >"; - level_string[1] = '0' + (loginfo->level % 8); spin_lock_bh(&log_lock); printk(level_string); printk("%sIN=%s OUT=%s ", - loginfo->prefix, + prefix == NULL ? loginfo->prefix : prefix, in ? in->name : "", out ? out->name : ""); if (in && !out) { @@ -329,10 +328,59 @@ dump_packet(loginfo, ipv6h, 1); printk("\n"); spin_unlock_bh(&log_lock); +} + +static unsigned int +ip6t_log_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ip6t_log_info *loginfo = targinfo; + char level_string[4] = "< >"; + + level_string[1] = '0' + (loginfo->level % 8); + ip6t_log_packet(pskb, hooknum, in, out, loginfo, level_string, NULL); return IP6T_CONTINUE; } +static void +ip6_log_packet_fn(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ + struct ip6t_log_info loginfo = { + .level = 0, + .logflags = IP6T_LOG_MASK, + .prefix = "" + }; + + ip6t_log_packet(pskb, hooknum, in, out, &loginfo, KERN_WARNING, prefix); +} + +static void +ip6_log_fn(char *pfh, size_t len, + const char *prefix) +{ + struct ipv6hdr *ipv6h = (struct ipv6hdr *)pfh; + struct ip6t_log_info loginfo = { + .level = 0, + .logflags = IP6T_LOG_MASK, + .prefix = "" + }; + + spin_lock_bh(&log_lock); + printk(KERN_WARNING "%s", prefix); + dump_packet(&loginfo, ipv6h, 1); + printk("\n"); + spin_unlock_bh(&log_lock); +} + static int ip6t_log_checkentry(const char *tablename, const struct ip6t_entry *e, void *targinfo, @@ -364,17 +412,21 @@ static struct ip6t_target ip6t_log_reg = { { NULL, NULL }, "LOG", ip6t_log_target, ip6t_log_checkentry, NULL, THIS_MODULE }; +static struct nf_logging_t ip6_logging_fn += { ip6_log_packet_fn, ip6_log_fn }; static int __init init(void) { if (ip6t_register_target(&ip6t_log_reg)) return -EINVAL; + nf_ip6_log_register(&ip6_logging_fn); return 0; } static void __exit fini(void) { + nf_ip6_log_unregister(&ip6_logging_fn); ip6t_unregister_target(&ip6t_log_reg); } diff -urN linux-2.4.26/net/ipv6/netfilter/ip6t_REJECT.c linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_REJECT.c --- linux-2.4.26/net/ipv6/netfilter/ip6t_REJECT.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_REJECT.c Sun Apr 18 17:33:03 2004 @@ -0,0 +1,458 @@ +/* + * IP6 tables REJECT target module + * Linux INET6 implementation + * + * Copyright (C)2003 USAGI/WIDE Project + * + * Authors: + * Yasuyuki Kozakai + * + * Based on net/ipv4/netfilter/ipt_REJECT.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Yasuyuki KOZAKAI "); +MODULE_DESCRIPTION("IP6 tables REJECT target module"); +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +#if 0 +static void connection_attach(struct sk_buff *new_skb, struct nf_ct_info *nfct) +{ + void (*attach)(struct sk_buff *, struct nf_ct_info *); + if (nfct && (attach = ip6_ct_attach) != NULL) { + mb(); + attach(new_skb, nfct); + } +} +#endif + +static int maybe_reroute(struct sk_buff *skb) +{ + if (skb->nfcache & NFC_ALTERED){ + if (ip6_route_me_harder(skb) != 0){ + kfree_skb(skb); + return -EINVAL; + } + } + + return dst_output(skb); +} + +/* Send RST reply */ +static void send_reset(struct sk_buff *oldskb) +{ + struct sk_buff *nskb; + struct tcphdr otcph, *tcph; + unsigned int otcplen, tcphoff, hh_len; + int needs_ack; + struct ipv6hdr *oip6h = oldskb->nh.ipv6h, *ip6h; + struct dst_entry *dst = NULL; + u8 proto; + struct flowi fl; + proto = oip6h->nexthdr; + int err; + + if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || + (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { + DEBUGP("ip6t_REJECT: addr is not unicast.\n"); + return; + } + + tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), + &proto, oldskb->len - ((u8*)(oip6h+1) + - oldskb->data)); + + if ((tcphoff < 0) || (tcphoff > oldskb->len)) { + DEBUGP("ip6t_REJECT: Can't get TCP header.\n"); + return; + } + + otcplen = oldskb->len - tcphoff; + + /* IP header checks: fragment, too short. */ + if ((proto != IPPROTO_TCP) || (otcplen < sizeof(struct tcphdr))) { + DEBUGP("ip6t_REJECT: proto(%d) != IPPROTO_TCP, or too short. otcplen = %d\n", + proto, otcplen); + return; + } + + if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) { + if (net_ratelimit()) + printk("ip6t_REJECT: Can't copy tcp header\n"); + return; + } + + /* No RST for RST. */ + if (otcph.rst) { + DEBUGP("ip6t_REJECT: RST is set\n"); + return; + } + + /* Check checksum. */ + if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP, + skb_checksum(oldskb, tcphoff, otcplen, 0))) { + DEBUGP("ip6t_REJECT: TCP checksum is invalid\n"); + return; + } + + memset(&fl, 0, sizeof(fl)); + fl.proto = IPPROTO_TCP; + ipv6_addr_copy(&fl.fl6_src, &oip6h->daddr); + ipv6_addr_copy(&fl.fl6_dst, &oip6h->saddr); + fl.fl_ip_sport = otcph.dest; + fl.fl_ip_dport = otcph.source; + err = ip6_dst_lookup(NULL, &dst, &fl); + if (err) { + if (net_ratelimit()) + printk("ip6t_REJECT: can't find dst. err = %d\n", err); + return; + } + + hh_len = (dst->dev->hard_header_len + 15)&~15; + nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) + + sizeof(struct tcphdr) + dst->trailer_len, + GFP_ATOMIC); + + if (!nskb) { + if (net_ratelimit()) + printk("ip6t_REJECT: Can't alloc skb\n"); + dst_release(dst); + return; + } + + nskb->dst = dst; + dst_hold(dst); + + skb_reserve(nskb, hh_len + dst->header_len); + + ip6h = nskb->nh.ipv6h = (struct ipv6hdr *) + skb_put(nskb, sizeof(struct ipv6hdr)); + ip6h->version = 6; + ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT); + ip6h->nexthdr = IPPROTO_TCP; + ip6h->payload_len = htons(sizeof(struct tcphdr)); + ipv6_addr_copy(&ip6h->saddr, &oip6h->daddr); + ipv6_addr_copy(&ip6h->daddr, &oip6h->saddr); + + tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); + /* Truncate to length (no data) */ + tcph->doff = sizeof(struct tcphdr)/4; + tcph->source = otcph.dest; + tcph->dest = otcph.source; + + if (otcph.ack) { + needs_ack = 0; + tcph->seq = otcph.ack_seq; + tcph->ack_seq = 0; + } else { + needs_ack = 1; + tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin + + otcplen - (otcph.doff<<2)); + tcph->seq = 0; + } + + /* Reset flags */ + ((u_int8_t *)tcph)[13] = 0; + tcph->rst = 1; + tcph->ack = needs_ack; + tcph->window = 0; + tcph->urg_ptr = 0; + tcph->check = 0; + + /* Adjust TCP checksum */ + tcph->check = csum_ipv6_magic(&nskb->nh.ipv6h->saddr, + &nskb->nh.ipv6h->daddr, + sizeof(struct tcphdr), IPPROTO_TCP, + csum_partial((char *)tcph, + sizeof(struct tcphdr), 0)); + +#if 0 + connection_attach(nskb, oldskb->nfct); +#endif + + NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, nskb, NULL, nskb->dst->dev, + maybe_reroute); + + dst_release(dst); +} + +static void send_unreach(struct sk_buff *skb_in, unsigned char code) +{ + struct ipv6hdr *ip6h, *hdr = skb_in->nh.ipv6h; + struct icmp6hdr *icmp6h; + struct dst_entry *dst = NULL; + struct rt6_info *rt; + int tmo; + __u32 csum; + unsigned int len, datalen, hh_len; + int saddr_type, daddr_type; + unsigned int ptr, ip6off; + u8 proto; + struct flowi fl; + struct sk_buff *nskb; + char *data; + + saddr_type = ipv6_addr_type(&hdr->saddr); + daddr_type = ipv6_addr_type(&hdr->daddr); + + if ((!(saddr_type & IPV6_ADDR_UNICAST)) || + (!(daddr_type & IPV6_ADDR_UNICAST))) { + DEBUGP("ip6t_REJECT: addr is not unicast.\n"); + return; + } + + ip6off = skb_in->nh.raw - skb_in->data; + proto = hdr->nexthdr; + ptr = ipv6_skip_exthdr(skb_in, ip6off + sizeof(struct ipv6hdr), &proto, + skb_in->len - ip6off); + + if ((ptr < 0) || (ptr > skb_in->len)) { + ptr = ip6off + sizeof(struct ipv6hdr); + proto = hdr->nexthdr; + } else if (proto == IPPROTO_ICMPV6) { + u8 type; + + if (skb_copy_bits(skb_in, ptr + offsetof(struct icmp6hdr, + icmp6_type), &type, 1)) { + DEBUGP("ip6t_REJECT: Can't get ICMPv6 type\n"); + return; + } + + if (!(type & ICMPV6_INFOMSG_MASK)) { + DEBUGP("ip6t_REJECT: no reply to icmp error\n"); + return; + } + } else if (proto == IPPROTO_UDP) { + int plen = skb_in->len - (ptr - ip6off); + uint16_t check; + + if (plen < sizeof(struct udphdr)) { + DEBUGP("ip6t_REJECT: too short\n"); + return; + } + + if (skb_copy_bits(skb_in, ptr + offsetof(struct udphdr, check), + &check, 2)) { + if (net_ratelimit()) + printk("ip6t_REJECT: can't get copy from skb"); + return; + } + + if (check && + csum_ipv6_magic(&hdr->saddr, &hdr->daddr, plen, + IPPROTO_UDP, + skb_checksum(skb_in, ptr, plen, 0))) { + DEBUGP("ip6t_REJECT: UDP checksum is invalid.\n"); + return; + } + } + + memset(&fl, 0, sizeof(fl)); + fl.proto = IPPROTO_ICMPV6; + ipv6_addr_copy(&fl.fl6_src, &hdr->daddr); + ipv6_addr_copy(&fl.fl6_dst, &hdr->saddr); + fl.fl_icmp_type = ICMPV6_DEST_UNREACH; + fl.fl_icmp_code = code; + + if (ip6_dst_lookup(NULL, &dst, &fl)) { + return; + } + + rt = (struct rt6_info *)dst; + tmo = 1*HZ; + + if (rt->rt6i_dst.plen < 128) + tmo >>= ((128 - rt->rt6i_dst.plen)>>5); + + if (!xrlim_allow(dst, tmo)) { + if (net_ratelimit()) + printk("ip6t_REJECT: rate limitted\n"); + goto dst_release_out; + } + + len = skb_in->len + sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr); + + if (len > dst_pmtu(dst)) + len = dst_pmtu(dst); + if (len > IPV6_MIN_MTU) + len = IPV6_MIN_MTU; + + datalen = len - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr); + hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15; + + nskb = alloc_skb(hh_len + 15 + dst->header_len + dst->trailer_len + len, + GFP_ATOMIC); + + if (!nskb) { + if (net_ratelimit()) + printk("ip6t_REJECT: can't alloc skb\n"); + goto dst_release_out; + } + + nskb->priority = 0; + nskb->dst = dst; + dst_hold(dst); + + skb_reserve(nskb, hh_len + dst->header_len); + + ip6h = nskb->nh.ipv6h = (struct ipv6hdr *) + skb_put(nskb, sizeof(struct ipv6hdr)); + ip6h->version = 6; + ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT); + ip6h->nexthdr = IPPROTO_ICMPV6; + ip6h->payload_len = htons(datalen + sizeof(struct icmp6hdr)); + ipv6_addr_copy(&ip6h->saddr, &hdr->daddr); + ipv6_addr_copy(&ip6h->daddr, &hdr->saddr); + + icmp6h = (struct icmp6hdr *) skb_put(nskb, sizeof(struct icmp6hdr)); + icmp6h->icmp6_type = ICMPV6_DEST_UNREACH; + icmp6h->icmp6_code = code; + icmp6h->icmp6_cksum = 0; + + data = skb_put(nskb, datalen); + + csum = csum_partial((unsigned char *)icmp6h, sizeof(struct icmp6hdr), 0); + csum = skb_copy_and_csum_bits(skb_in, ip6off, data, datalen, csum); + icmp6h->icmp6_cksum = csum_ipv6_magic(&hdr->saddr, &hdr->daddr, + datalen + sizeof(struct icmp6hdr), + IPPROTO_ICMPV6, csum); + +#if 0 + connection_attach(nskb, skb_in->nfct); +#endif + NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, nskb, NULL, nskb->dst->dev, + maybe_reroute); + +dst_release_out: + dst_release(dst); +} + +static unsigned int reject6_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ip6t_reject_info *reject = targinfo; + + DEBUGP(KERN_DEBUG "%s: medium point\n", __FUNCTION__); + /* WARNING: This code causes reentry within ip6tables. + This means that the ip6tables jump stack is now crap. We + must return an absolute verdict. --RR */ + switch (reject->with) { + case IP6T_ICMP6_NO_ROUTE: + send_unreach(*pskb, ICMPV6_NOROUTE); + break; + case IP6T_ICMP6_ADM_PROHIBITED: + send_unreach(*pskb, ICMPV6_ADM_PROHIBITED); + break; + case IP6T_ICMP6_NOT_NEIGHBOUR: + send_unreach(*pskb, ICMPV6_NOT_NEIGHBOUR); + break; + case IP6T_ICMP6_ADDR_UNREACH: + send_unreach(*pskb, ICMPV6_ADDR_UNREACH); + break; + case IP6T_ICMP6_PORT_UNREACH: + send_unreach(*pskb, ICMPV6_PORT_UNREACH); + break; + case IP6T_ICMP6_ECHOREPLY: + /* Do nothing */ + break; + case IP6T_TCP_RESET: + send_reset(*pskb); + break; + default: + if (net_ratelimit()) + printk(KERN_WARNING "ip6t_REJECT: case %u not handled yet\n", reject->with); + break; + } + + return NF_DROP; +} + +static int check(const char *tablename, + const struct ip6t_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + const struct ip6t_reject_info *rejinfo = targinfo; + + if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_reject_info))) { + DEBUGP("ip6t_REJECT: targinfosize %u != 0\n", targinfosize); + return 0; + } + + /* Only allow these for packet filtering. */ + if (strcmp(tablename, "filter") != 0) { + DEBUGP("ip6t_REJECT: bad table `%s'.\n", tablename); + return 0; + } + + if ((hook_mask & ~((1 << NF_IP6_LOCAL_IN) + | (1 << NF_IP6_FORWARD) + | (1 << NF_IP6_LOCAL_OUT))) != 0) { + DEBUGP("ip6t_REJECT: bad hook mask %X\n", hook_mask); + return 0; + } + + if (rejinfo->with == IP6T_ICMP6_ECHOREPLY) { + printk("ip6t_REJECT: ECHOREPLY is not supported.\n"); + return 0; + } else if (rejinfo->with == IP6T_TCP_RESET) { + /* Must specify that it's a TCP packet */ + if (e->ipv6.proto != IPPROTO_TCP + || (e->ipv6.invflags & IP6T_INV_PROTO)) { + DEBUGP("ip6t_REJECT: TCP_RESET illegal for non-tcp\n"); + return 0; + } + } + + return 1; +} + +static struct ip6t_target ip6t_reject_reg = { + .name = "REJECT", + .target = reject6_target, + .checkentry = check, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + if (ip6t_register_target(&ip6t_reject_reg)) + return -EINVAL; + return 0; +} + +static void __exit fini(void) +{ + ip6t_unregister_target(&ip6t_reject_reg); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv6/netfilter/ip6t_ROUTE.c linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_ROUTE.c --- linux-2.4.26/net/ipv6/netfilter/ip6t_ROUTE.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_ROUTE.c Sun Apr 18 17:33:03 2004 @@ -0,0 +1,289 @@ +/* + * This implements the ROUTE v6 target, which enables you to setup unusual + * routes not supported by the standard kernel routing table. + * + * Copyright (C) 2003 Cedric de Launois + * + * v 1.0 2003/08/05 + * + * This software is distributed under GNU GPL v2, 1991 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 1 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +#define NIP6(addr) \ + ntohs((addr).s6_addr16[0]), \ + ntohs((addr).s6_addr16[1]), \ + ntohs((addr).s6_addr16[2]), \ + ntohs((addr).s6_addr16[3]), \ + ntohs((addr).s6_addr16[4]), \ + ntohs((addr).s6_addr16[5]), \ + ntohs((addr).s6_addr16[6]), \ + ntohs((addr).s6_addr16[7]) + +/* Route the packet according to the routing keys specified in + * route_info. Keys are : + * - ifindex : + * 0 if no oif preferred, + * otherwise set to the index of the desired oif + * - route_info->gw : + * 0 if no gateway specified, + * otherwise set to the next host to which the pkt must be routed + * If success, skb->dev is the output device to which the packet must + * be sent and skb->dst is not NULL + * + * RETURN: 1 if the packet was succesfully routed to the + * destination desired + * 0 if the kernel routing table could not route the packet + * according to the keys specified + */ +static int +route6(struct sk_buff *skb, + unsigned int ifindex, + const struct ip6t_route_target_info *route_info) +{ + struct rt6_info *rt = NULL; + struct ipv6hdr *ipv6h = skb->nh.ipv6h; + struct in6_addr *gw = (struct in6_addr*)&route_info->gw; + + DEBUGP("ip6t_ROUTE: called with: "); + DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr)); + DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(*gw)); + DEBUGP("OUT=%s\n", route_info->oif); + + if (ipv6_addr_any(gw)) + rt = rt6_lookup(&ipv6h->daddr, &ipv6h->saddr, ifindex, 1); + else + rt = rt6_lookup(gw, &ipv6h->saddr, ifindex, 1); + + if (!rt) + goto no_route; + + DEBUGP("ip6t_ROUTE: routing gives: "); + DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_dst.addr)); + DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_gateway)); + DEBUGP("OUT=%s\n", rt->rt6i_dev->name); + + if (ifindex && rt->rt6i_dev->ifindex!=ifindex) + goto wrong_route; + + if (!rt->rt6i_nexthop) { + DEBUGP("ip6t_ROUTE: discovering neighbour\n"); + rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_dst.addr); + } + + /* Drop old route. */ + dst_release(skb->dst); + skb->dst = &rt->u.dst; + skb->dev = rt->rt6i_dev; + return 1; + + wrong_route: + dst_release(&rt->u.dst); + no_route: + if (!net_ratelimit()) + return 0; + + printk("ip6t_ROUTE: no explicit route found "); + if (ifindex) + printk("via interface %s ", route_info->oif); + if (!ipv6_addr_any(gw)) + printk("via gateway %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", NIP6(*gw)); + printk("\n"); + return 0; +} + + +/* Stolen from ip6_output_finish + * PRE : skb->dev is set to the device we are leaving by + * skb->dst is not NULL + * POST: the packet is sent with the link layer header pushed + * the packet is destroyed + */ +static void ip_direct_send(struct sk_buff *skb) +{ + struct dst_entry *dst = skb->dst; + struct hh_cache *hh = dst->hh; + + if (hh) { + read_lock_bh(&hh->hh_lock); + memcpy(skb->data - 16, hh->hh_data, 16); + read_unlock_bh(&hh->hh_lock); + skb_push(skb, hh->hh_len); + hh->hh_output(skb); + } else if (dst->neighbour) + dst->neighbour->output(skb); + else { + if (net_ratelimit()) + DEBUGP(KERN_DEBUG "ip6t_ROUTE: no hdr & no neighbour cache!\n"); + kfree_skb(skb); + } +} + + +static unsigned int +route6_oif(const struct ip6t_route_target_info *route_info, + struct sk_buff *skb) +{ + unsigned int ifindex = 0; + struct net_device *dev_out = NULL; + + /* The user set the interface name to use. + * Getting the current interface index. + */ + if ((dev_out = dev_get_by_name(route_info->oif))) { + ifindex = dev_out->ifindex; + } else { + /* Unknown interface name : packet dropped */ + if (net_ratelimit()) + DEBUGP("ip6t_ROUTE: oif interface %s not found\n", route_info->oif); + + if (route_info->flags & IP6T_ROUTE_CONTINUE) + return IP6T_CONTINUE; + else + return NF_DROP; + } + + /* Trying the standard way of routing packets */ + if (route6(skb, ifindex, route_info)) { + dev_put(dev_out); + if (route_info->flags & IP6T_ROUTE_CONTINUE) + return IP6T_CONTINUE; + + ip_direct_send(skb); + return NF_STOLEN; + } else + return NF_DROP; +} + + +static unsigned int +route6_gw(const struct ip6t_route_target_info *route_info, + struct sk_buff *skb) +{ + if (route6(skb, 0, route_info)) { + if (route_info->flags & IP6T_ROUTE_CONTINUE) + return IP6T_CONTINUE; + + ip_direct_send(skb); + return NF_STOLEN; + } else + return NF_DROP; +} + + +static unsigned int +ip6t_route_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ip6t_route_target_info *route_info = targinfo; + struct sk_buff *skb = *pskb; + struct in6_addr *gw = (struct in6_addr*)&route_info->gw; + + if (route_info->flags & IP6T_ROUTE_CONTINUE) + goto do_it; + + /* If we are at PREROUTING or INPUT hook + * the TTL isn't decreased by the IP stack + */ + if (hooknum == NF_IP6_PRE_ROUTING || + hooknum == NF_IP6_LOCAL_IN) { + + struct ipv6hdr *ipv6h = skb->nh.ipv6h; + + if (ipv6h->hop_limit <= 1) { + /* Force OUTPUT device used as source address */ + skb->dev = skb->dst->dev; + + icmpv6_send(skb, ICMPV6_TIME_EXCEED, + ICMPV6_EXC_HOPLIMIT, 0, skb->dev); + + return NF_DROP; + } + + ipv6h->hop_limit--; + } + + + do_it: + if (route_info->oif[0]) + return route6_oif(route_info, *pskb); + + if (!ipv6_addr_any(gw)) + return route6_gw(route_info, *pskb); + + if (net_ratelimit()) + DEBUGP(KERN_DEBUG "ip6t_ROUTE: no parameter !\n"); + + return IP6T_CONTINUE; +} + + +static int +ip6t_route_checkentry(const char *tablename, + const struct ip6t_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (strcmp(tablename, "mangle") != 0) { + printk("ip6t_ROUTE: can only be called from \"mangle\" table.\n"); + return 0; + } + + if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_route_target_info))) { + printk(KERN_WARNING "ip6t_ROUTE: targinfosize %u != %Zu\n", + targinfosize, + IP6T_ALIGN(sizeof(struct ip6t_route_target_info))); + return 0; + } + + return 1; +} + + +static struct ip6t_target ip6t_route_reg = { + .name = "ROUTE", + .target = ip6t_route_target, + .checkentry = ip6t_route_checkentry, + .me = THIS_MODULE +}; + + +static int __init init(void) +{ + printk(KERN_DEBUG "registering ipv6 ROUTE target\n"); + if (ip6t_register_target(&ip6t_route_reg)) + return -EINVAL; + + return 0; +} + + +static void __exit fini(void) +{ + ip6t_unregister_target(&ip6t_route_reg); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.26/net/ipv6/netfilter/ip6t_condition.c linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_condition.c --- linux-2.4.26/net/ipv6/netfilter/ip6t_condition.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_condition.c Sun Apr 18 17:33:05 2004 @@ -0,0 +1,254 @@ +/*-------------------------------------------*\ +| Netfilter Condition Module for IPv6 | +| | +| Description: This module allows firewall | +| rules to match using condition variables | +| stored in /proc files. | +| | +| Author: Stephane Ouellette 2003-02-10 | +| | +| | +| This software is distributed under the | +| terms of the GNU GPL. | +\*-------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include + + +#ifndef CONFIG_PROC_FS +#error "Proc file system support is required for this module" +#endif + + +MODULE_AUTHOR("Stephane Ouellette "); +MODULE_DESCRIPTION("Allows rules to match against condition variables"); +MODULE_LICENSE("GPL"); + + +struct condition_variable { + struct condition_variable *next; + struct proc_dir_entry *status_proc; + atomic_t refcount; + int enabled; /* TRUE == 1, FALSE == 0 */ +}; + + +static rwlock_t list_lock; +static struct condition_variable *head = NULL; +static struct proc_dir_entry *proc_net_condition = NULL; + + +static int +ipt_condition_read_info(char *buffer, char **start, off_t offset, + int length, int *eof, void *data) +{ + struct condition_variable *var = + (struct condition_variable *) data; + + if (offset == 0) { + *start = buffer; + buffer[0] = (var->enabled) ? '1' : '0'; + buffer[1] = '\n'; + return 2; + } + + *eof = 1; + return 0; +} + + +static int +ipt_condition_write_info(struct file *file, const char *buffer, + unsigned long length, void *data) +{ + struct condition_variable *var = + (struct condition_variable *) data; + + if (length) { + /* Match only on the first character */ + switch (buffer[0]) { + case '0': + var->enabled = 0; + break; + case '1': + var->enabled = 1; + } + } + + return (int) length; +} + + +static int +match(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const void *matchinfo, int offset, + const void *hdr, u_int16_t datalen, int *hotdrop) +{ + const struct condition6_info *info = + (const struct condition6_info *) matchinfo; + struct condition_variable *var; + int condition_status = 0; + + read_lock(&list_lock); + + for (var = head; var; var = var->next) { + if (strcmp(info->name, var->status_proc->name) == 0) { + condition_status = var->enabled; + break; + } + } + + read_unlock(&list_lock); + + return condition_status ^ info->invert; +} + + + +static int +checkentry(const char *tablename, const struct ip6t_ip6 *ip, + void *matchinfo, unsigned int matchsize, unsigned int hook_mask) +{ + struct condition6_info *info = + (struct condition6_info *) matchinfo; + struct condition_variable *var, *newvar; + + if (matchsize != IP6T_ALIGN(sizeof(struct condition6_info))) + return 0; + + /* The first step is to check if the condition variable already exists. */ + /* Here, a read lock is sufficient because we won't change the list */ + read_lock(&list_lock); + + for (var = head; var; var = var->next) { + if (strcmp(info->name, var->status_proc->name) == 0) { + atomic_inc(&var->refcount); + read_unlock(&list_lock); + return 1; + } + } + + read_unlock(&list_lock); + + /* At this point, we need to allocate a new condition variable */ + newvar = kmalloc(sizeof(struct condition_variable), GFP_KERNEL); + + if (!newvar) + return -ENOMEM; + + /* Create the condition variable's proc file entry */ + newvar->status_proc = create_proc_entry(info->name, 0644, proc_net_condition); + + if (!newvar->status_proc) { + /* + * There are two possibilities: + * 1- Another condition variable with the same name has been created, which is valid. + * 2- There was a memory allocation error. + */ + kfree(newvar); + read_lock(&list_lock); + + for (var = head; var; var = var->next) { + if (strcmp(info->name, var->status_proc->name) == 0) { + atomic_inc(&var->refcount); + read_unlock(&list_lock); + return 1; + } + } + + read_unlock(&list_lock); + return -ENOMEM; + } + + atomic_set(&newvar->refcount, 1); + newvar->enabled = 0; + newvar->status_proc->owner = THIS_MODULE; + newvar->status_proc->data = newvar; + wmb(); + newvar->status_proc->read_proc = ipt_condition_read_info; + newvar->status_proc->write_proc = ipt_condition_write_info; + + write_lock(&list_lock); + + newvar->next = head; + head = newvar; + + write_unlock(&list_lock); + + return 1; +} + + +static void +destroy(void *matchinfo, unsigned int matchsize) +{ + struct condition6_info *info = + (struct condition6_info *) matchinfo; + struct condition_variable *var, *prev = NULL; + + if (matchsize != IP6T_ALIGN(sizeof(struct condition6_info))) + return; + + write_lock(&list_lock); + + for (var = head; var && strcmp(info->name, var->status_proc->name); + prev = var, var = var->next); + + if (var && atomic_dec_and_test(&var->refcount)) { + if (prev) + prev->next = var->next; + else + head = var->next; + + write_unlock(&list_lock); + remove_proc_entry(var->status_proc->name, proc_net_condition); + kfree(var); + } else + write_unlock(&list_lock); +} + + +static struct ip6t_match condition_match = { + .name = "condition", + .match = &match, + .checkentry = &checkentry, + .destroy = &destroy, + .me = THIS_MODULE +}; + + +static int __init +init(void) +{ + int errorcode; + + rwlock_init(&list_lock); + proc_net_condition = proc_mkdir("ip6t_condition", proc_net); + + if (proc_net_condition) { + errorcode = ipt_register_match(&condition_match); + + if (errorcode) + remove_proc_entry("ip6t_condition", proc_net); + } else + errorcode = -EACCES; + + return errorcode; +} + + +static void __exit +fini(void) +{ + ipt_unregister_match(&condition_match); + remove_proc_entry("ip6t_condition", proc_net); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv6/netfilter/ip6t_nth.c linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_nth.c --- linux-2.4.26/net/ipv6/netfilter/ip6t_nth.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_nth.c Sun Apr 18 17:33:07 2004 @@ -0,0 +1,173 @@ +/* + This is a module which is used for match support for every Nth packet + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 2001-07-18 Fabrice MARIE : initial implementation. + 2001-09-20 Richard Wagner (rwagner@cloudnet.com) + * added support for multiple counters + * added support for matching on individual packets + in the counter cycle + 2003-04-30 Maciej Soltysiak : IPv6 Port + +*/ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +/* + * State information. + */ +struct state { + spinlock_t lock; + u_int16_t number; +}; + +static struct state states[IP6T_NTH_NUM_COUNTERS]; + +static int +ip6t_nth_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + /* Parameters from userspace */ + const struct ip6t_nth_info *info = matchinfo; + unsigned counter = info->counter; + if((counter < 0) || (counter >= IP6T_NTH_NUM_COUNTERS)) + { + printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IP6T_NTH_NUM_COUNTERS-1); + return 0; + }; + + spin_lock(&states[counter].lock); + + /* Are we matching every nth packet?*/ + if (info->packet == 0xFF) + { + /* We're matching every nth packet and only every nth packet*/ + /* Do we match or invert match? */ + if (info->not == 0) + { + if (states[counter].number == 0) + { + ++states[counter].number; + goto match; + } + if (states[counter].number >= info->every) + states[counter].number = 0; /* reset the counter */ + else + ++states[counter].number; + goto dontmatch; + } + else + { + if (states[counter].number == 0) + { + ++states[counter].number; + goto dontmatch; + } + if (states[counter].number >= info->every) + states[counter].number = 0; + else + ++states[counter].number; + goto match; + } + } + else + { + /* We're using the --packet, so there must be a rule for every value */ + if (states[counter].number == info->packet) + { + /* only increment the counter when a match happens */ + if (states[counter].number >= info->every) + states[counter].number = 0; /* reset the counter */ + else + ++states[counter].number; + goto match; + } + else + goto dontmatch; + } + + dontmatch: + /* don't match */ + spin_unlock(&states[counter].lock); + return 0; + + match: + spin_unlock(&states[counter].lock); + return 1; +} + +static int +ip6t_nth_checkentry(const char *tablename, + const struct ip6t_ip6 *e, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + /* Parameters from userspace */ + const struct ip6t_nth_info *info = matchinfo; + unsigned counter = info->counter; + if((counter < 0) || (counter >= IP6T_NTH_NUM_COUNTERS)) + { + printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IP6T_NTH_NUM_COUNTERS-1); + return 0; + }; + + if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_nth_info))) { + printk("nth: matchsize %u != %u\n", matchsize, + IP6T_ALIGN(sizeof(struct ip6t_nth_info))); + return 0; + } + + states[counter].number = info->startat; + + return 1; +} + +static struct ip6t_match ip6t_nth_reg = { + {NULL, NULL}, + "nth", + ip6t_nth_match, + ip6t_nth_checkentry, + NULL, + THIS_MODULE }; + +static int __init init(void) +{ + unsigned counter; + memset(&states, 0, sizeof(states)); + if (ip6t_register_match(&ip6t_nth_reg)) + return -EINVAL; + + for(counter = 0; counter < IP6T_NTH_NUM_COUNTERS; counter++) + { + spin_lock_init(&(states[counter].lock)); + }; + + printk("ip6t_nth match loaded\n"); + return 0; +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&ip6t_nth_reg); + printk("ip6t_nth match unloaded\n"); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/ipv6/netfilter/ip6t_owner.c linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_owner.c --- linux-2.4.26/net/ipv6/netfilter/ip6t_owner.c Wed Oct 31 00:08:12 2001 +++ linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_owner.c Sun Apr 18 17:33:08 2004 @@ -16,6 +16,38 @@ MODULE_LICENSE("GPL"); static int +match_comm(const struct sk_buff *skb, const char *comm) +{ + struct task_struct *p; + struct files_struct *files; + int i; + + read_lock(&tasklist_lock); + for_each_task(p) { + if(strncmp(p->comm, comm, sizeof(p->comm))) + continue; + + task_lock(p); + files = p->files; + if(files) { + read_lock(&files->file_lock); + for (i=0; i < files->max_fds; i++) { + if (fcheck_files(files, i) == skb->sk->socket->file) { + read_unlock(&files->file_lock); + task_unlock(p); + read_unlock(&tasklist_lock); + return 1; + } + } + read_unlock(&files->file_lock); + } + task_unlock(p); + } + read_unlock(&tasklist_lock); + return 0; +} + +static int match_pid(const struct sk_buff *skb, pid_t pid) { struct task_struct *p; @@ -116,6 +148,12 @@ if(info->match & IP6T_OWNER_SID) { if (!match_sid(skb, info->sid) ^ !!(info->invert & IP6T_OWNER_SID)) + return 0; + } + + if(info->match & IP6T_OWNER_COMM) { + if (!match_comm(skb, info->comm) ^ + !!(info->invert & IP6T_OWNER_COMM)) return 0; } diff -urN linux-2.4.26/net/ipv6/netfilter/ip6t_random.c linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_random.c --- linux-2.4.26/net/ipv6/netfilter/ip6t_random.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-pomng/net/ipv6/netfilter/ip6t_random.c Sun Apr 18 17:33:09 2004 @@ -0,0 +1,97 @@ +/* + This is a module which is used for a "random" match support. + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 2001-10-14 Fabrice MARIE : initial implementation. + 2003-04-30 Maciej Soltysiak : IPv6 Port +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +static int +ip6t_rand_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + /* Parameters from userspace */ + const struct ip6t_rand_info *info = matchinfo; + u_int8_t random_number; + + /* get 1 random number from the kernel random number generation routine */ + get_random_bytes((void *)(&random_number), 1); + + /* Do we match ? */ + if (random_number <= info->average) + return 1; + else + return 0; +} + +static int +ip6t_rand_checkentry(const char *tablename, + const struct ip6t_ip6 *e, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + /* Parameters from userspace */ + const struct ip6t_rand_info *info = matchinfo; + + if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_rand_info))) { + printk("ip6t_random: matchsize %u != %u\n", matchsize, + IP6T_ALIGN(sizeof(struct ip6t_rand_info))); + return 0; + } + + /* must be 1 <= average % <= 99 */ + /* 1 x 2.55 = 2 */ + /* 99 x 2.55 = 252 */ + if ((info->average < 2) || (info->average > 252)) { + printk("ip6t_random: invalid average %u\n", info->average); + return 0; + } + + return 1; +} + +static struct ip6t_match ip6t_rand_reg = { + {NULL, NULL}, + "random", + ip6t_rand_match, + ip6t_rand_checkentry, + NULL, + THIS_MODULE }; + +static int __init init(void) +{ + if (ip6t_register_match(&ip6t_rand_reg)) + return -EINVAL; + + printk("ip6t_random match loaded\n"); + return 0; +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&ip6t_rand_reg); + printk("ip6t_random match unloaded\n"); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.26/net/netsyms.c linux-2.4.26-pomng/net/netsyms.c --- linux-2.4.26/net/netsyms.c Sat Apr 17 23:05:49 2004 +++ linux-2.4.26-pomng/net/netsyms.c Sun Apr 18 17:33:08 2004 @@ -590,6 +590,7 @@ #endif #ifdef CONFIG_NETFILTER #include +#include EXPORT_SYMBOL(nf_register_hook); EXPORT_SYMBOL(nf_unregister_hook); EXPORT_SYMBOL(nf_register_sockopt); @@ -602,6 +603,10 @@ EXPORT_SYMBOL(nf_setsockopt); EXPORT_SYMBOL(nf_getsockopt); EXPORT_SYMBOL(ip_ct_attach); +EXPORT_SYMBOL(nf_log_register); +EXPORT_SYMBOL(nf_log_unregister); +EXPORT_SYMBOL(nf_log_packet); +EXPORT_SYMBOL(nf_log); #ifdef CONFIG_INET #include EXPORT_SYMBOL(ip_route_me_harder); @@ -628,5 +633,13 @@ EXPORT_SYMBOL(ethtool_op_set_tx_csum); EXPORT_SYMBOL(ethtool_op_get_sg); EXPORT_SYMBOL(ethtool_op_set_sg); + +#if defined(CONFIG_IP_NF_MATCH_OWNER)||defined(CONFIG_IP_NF_MATCH_OWNER_MODULE) +EXPORT_SYMBOL(tcp_v4_lookup); +EXPORT_SYMBOL(udp_v4_lookup); +#if !(defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)) +EXPORT_SYMBOL(tcp_timewait_cachep); +#endif +#endif /* CONFIG_IP_NF_MATCH_OWNER */ #endif /* CONFIG_NET */