diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack.h Sat Aug 3 02:39:45 2002 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack.h Fri Nov 29 00:53:15 2002 @@ -6,6 +6,7 @@ #include #include +#include enum ip_conntrack_info { @@ -42,12 +43,57 @@ IPS_ASSURED = (1 << IPS_ASSURED_BIT), }; +#include +#include + +/* per conntrack: protocol private data */ +union ip_conntrack_proto { + /* insert conntrack proto private data here */ + struct ip_ct_tcp tcp; + struct ip_ct_icmp icmp; +}; + +union ip_conntrack_expect_proto { + /* insert expect proto private data here */ +}; + +/* Add protocol helper include file here */ +#include +#include + +/* per expectation: application helper private data */ +union ip_conntrack_expect_help { + /* insert conntrack helper private data (expect) here */ + struct ip_ct_ftp_expect exp_ftp_info; + struct ip_ct_irc_expect exp_irc_info; + +#ifdef CONFIG_IP_NF_NAT_NEEDED + union { + /* insert nat helper private data (expect) here */ + } nat; +#endif +}; + +/* per conntrack: application helper private data */ +union ip_conntrack_help { + /* insert conntrack helper private data (master) here */ + struct ip_ct_ftp_master ct_ftp_info; + struct ip_ct_irc_master ct_irc_info; +}; + +#ifdef CONFIG_IP_NF_NAT_NEEDED +#include + +/* per conntrack: nat application helper private data */ +union ip_conntrack_nat_help { + /* insert nat helper private data here */ +}; +#endif + #ifdef __KERNEL__ #include #include -#include -#include #ifdef CONFIG_NF_DEBUG #define IP_NF_ASSERT(x) \ @@ -64,26 +110,45 @@ struct ip_conntrack_expect { - /* Internal linked list */ + /* Internal linked list (global expectation list) */ struct list_head list; + /* reference count */ + atomic_t use; + + /* expectation list for this master */ + struct list_head expected_list; + + /* The conntrack of the master connection */ + struct ip_conntrack *expectant; + + /* The conntrack of the sibling connection, set after + * expectation arrived */ + struct ip_conntrack *sibling; + + /* Tuple saved for conntrack */ + struct ip_conntrack_tuple ct_tuple; + + /* Timer function; deletes the expectation. */ + struct timer_list timeout; + + /* Data filled out by the conntrack helpers follow: */ + /* We expect this tuple, with the following mask */ struct ip_conntrack_tuple tuple, mask; /* Function to call after setup and insertion */ int (*expectfn)(struct ip_conntrack *new); - /* The conntrack we are part of (set iff we're live) */ - struct ip_conntrack *expectant; -}; + /* At which sequence number did this expectation occur */ + u_int32_t seq; + + union ip_conntrack_expect_proto proto; -#ifdef CONFIG_IP_NF_NAT_NEEDED -#include -#endif - -#include -#include + union ip_conntrack_expect_help help; +}; +#include struct ip_conntrack { /* Usage count in here is 1 for hash table/destruct timer, 1 per skb, @@ -101,10 +166,13 @@ /* If we're expecting another related connection, this will be in expected linked list */ - struct ip_conntrack_expect expected; + struct list_head sibling_list; + + /* Current number of expected connections */ + unsigned int expecting; - /* If we were expected by another connection, this will be it */ - struct nf_ct_info master; + /* If we were expected by an expectation, this will be it */ + struct ip_conntrack_expect *master; /* Helper, if any. */ struct ip_conntrack_helper *helper; @@ -115,22 +183,14 @@ /* Storage reserved for other modules: */ - union { - struct ip_ct_tcp tcp; - struct ip_ct_icmp icmp; - } proto; + union ip_conntrack_proto proto; - union { - struct ip_ct_ftp ct_ftp_info; - struct ip_ct_irc ct_irc_info; - } help; + union ip_conntrack_help help; #ifdef CONFIG_IP_NF_NAT_NEEDED struct { struct ip_nat_info info; - union { - /* insert nat helper private data here */ - } help; + union ip_conntrack_nat_help help; #if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \ defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE) int masq_index; @@ -140,6 +200,9 @@ }; +/* get master conntrack via master expectation */ +#define master_ct(conntr) (conntr->master ? conntr->master->expectant : NULL) + /* Alter reply tuple (maybe alter helper). If it's already taken, return 0 and don't do alteration. */ extern int @@ -155,6 +218,16 @@ /* Return conntrack_info and tuple hash for given skb. */ extern struct ip_conntrack * ip_conntrack_get(struct sk_buff *skb, enum ip_conntrack_info *ctinfo); + +/* decrement reference count on a conntrack */ +extern inline void ip_conntrack_put(struct ip_conntrack *ct); + +/* find unconfirmed expectation based on tuple */ +struct ip_conntrack_expect * +ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple); + +/* decrement reference count on an expectation */ +void ip_conntrack_expect_put(struct ip_conntrack_expect *exp); extern struct module *ip_conntrack_module; diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_core.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_core.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_core.h Fri Apr 27 23:15:01 2001 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_core.h Fri Nov 29 00:53:15 2002 @@ -15,9 +15,9 @@ extern void ip_conntrack_cleanup(void); struct ip_conntrack_protocol; -extern struct ip_conntrack_protocol *find_proto(u_int8_t protocol); +extern struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol); /* Like above, but you already have conntrack read lock. */ -extern struct ip_conntrack_protocol *__find_proto(u_int8_t protocol); +extern struct 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,7 +45,7 @@ } extern struct list_head *ip_conntrack_hash; -extern struct list_head expect_list; +extern struct list_head ip_conntrack_expect_list; DECLARE_RWLOCK_EXTERN(ip_conntrack_lock); #endif /* _IP_CONNTRACK_CORE_H */ diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_ftp.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_ftp.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_ftp.h Thu Apr 26 00:00:28 2001 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_ftp.h Fri Nov 29 00:53:15 2002 @@ -2,15 +2,17 @@ #define _IP_CONNTRACK_FTP_H /* FTP tracking. */ -#ifndef __KERNEL__ -#error Only in kernel. -#endif +#ifdef __KERNEL__ #include /* Protects ftp part of conntracks */ DECLARE_LOCK_EXTERN(ip_ftp_lock); +#define FTP_PORT 21 + +#endif /* __KERNEL__ */ + enum ip_ct_ftp_type { /* PORT command from client */ @@ -23,18 +25,20 @@ IP_CT_FTP_EPSV, }; -/* We record seq number and length of ftp ip/port text here: all in - host order. */ -struct ip_ct_ftp +/* This structure is per expected connection */ +struct ip_ct_ftp_expect { - /* This tells NAT that this is an ftp connection */ - int is_ftp; - u_int32_t seq; - /* 0 means not found yet */ - u_int32_t len; - enum ip_ct_ftp_type ftptype; - /* Port that was to be used */ - u_int16_t port; + /* We record seq number and length of ftp ip/port text here: all in + * host order. */ + + /* sequence number of IP address in packet is in ip_conntrack_expect */ + u_int32_t len; /* length of IP address */ + enum ip_ct_ftp_type ftptype; /* PORT or PASV ? */ + u_int16_t port; /* TCP port that was to be used */ +}; + +/* This structure exists only once per master */ +struct ip_ct_ftp_master { /* Next valid seq position for cmd matching after newline */ u_int32_t seq_aft_nl[IP_CT_DIR_MAX]; /* 0 means seq_match_aft_nl not set */ diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_helper.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_helper.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_helper.h Mon Dec 11 22:31:23 2000 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_helper.h Fri Nov 29 00:53:15 2002 @@ -5,10 +5,19 @@ struct module; +/* Reuse expectation when max_expected reached */ +#define IP_CT_HELPER_F_REUSE_EXPECT 0x01 + struct ip_conntrack_helper { - /* Internal use. */ - struct list_head list; + struct list_head list; /* Internal use. */ + + const char *name; /* name of the module */ + unsigned char flags; /* Flags (see above) */ + struct module *me; /* pointer to self */ + unsigned int max_expected; /* Maximum number of concurrent + * expected connections */ + unsigned int timeout; /* timeout for expecteds */ /* Mask of things we will help (compared against server response) */ struct ip_conntrack_tuple tuple; @@ -24,11 +33,13 @@ extern int ip_conntrack_helper_register(struct ip_conntrack_helper *); extern void ip_conntrack_helper_unregister(struct ip_conntrack_helper *); -/* Add an expected connection: can only have one per connection */ +extern struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple); + +/* Add an expected connection: can have more than one per connection */ extern int ip_conntrack_expect_related(struct ip_conntrack *related_to, - const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *mask, - int (*expectfn)(struct ip_conntrack *)); -extern void ip_conntrack_unexpect_related(struct ip_conntrack *related_to); + struct ip_conntrack_expect *exp); +extern int ip_conntrack_change_expect(struct ip_conntrack_expect *expect, + struct ip_conntrack_tuple *newtuple); +extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp); #endif /*_IP_CONNTRACK_HELPER_H*/ diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_irc.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_irc.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_irc.h Wed Oct 31 00:08:12 2001 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_irc.h Fri Nov 29 00:53:15 2002 @@ -14,13 +14,28 @@ #ifndef _IP_CONNTRACK_IRC_H #define _IP_CONNTRACK_IRC_H -#ifndef __KERNEL__ -#error Only in kernel. -#endif +/* We record seq number and length of irc ip/port text here: all in + host order. */ + +/* This structure is per expected connection */ +struct ip_ct_irc_expect +{ + /* length of IP address */ + u_int32_t len; + /* Port that was to be used */ + u_int16_t port; +}; + +/* This structure exists only once per master */ +struct ip_ct_irc_master { +}; + + +#ifdef __KERNEL__ #include -#define IP_CONNTR_IRC 2 +#define IRC_PORT 6667 struct dccproto { char* match; @@ -30,18 +45,6 @@ /* Protects irc part of conntracks */ DECLARE_LOCK_EXTERN(ip_irc_lock); -/* We record seq number and length of irc ip/port text here: all in - host order. */ -struct ip_ct_irc -{ - /* This tells NAT that this is an IRC connection */ - int is_irc; - /* sequence number where address part of DCC command begins */ - u_int32_t seq; - /* 0 means not found yet */ - u_int32_t len; - /* Port that was to be used */ - u_int16_t port; -}; +#endif /* __KERNEL__ */ #endif /* _IP_CONNTRACK_IRC_H */ diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_protocol.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_protocol.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_protocol.h Sat Aug 3 02:39:45 2002 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_protocol.h Fri Nov 29 00:53:15 2002 @@ -45,6 +45,10 @@ /* Called when a conntrack entry is destroyed */ void (*destroy)(struct ip_conntrack *conntrack); + /* Has to decide if a expectation matches one packet or not */ + int (*exp_matches_pkt)(struct ip_conntrack_expect *exp, + struct sk_buff **pskb); + /* Module (if any) which this is connected to. */ struct module *me; }; diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_tcp.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_tcp.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_conntrack_tcp.h Fri Aug 4 22:07:24 2000 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_conntrack_tcp.h Fri Nov 29 00:53:15 2002 @@ -2,10 +2,6 @@ #define _IP_CONNTRACK_TCP_H /* TCP tracking. */ -#ifndef __KERNEL__ -#error Only in kernel. -#endif - enum tcp_conntrack { TCP_CONNTRACK_NONE, TCP_CONNTRACK_ESTABLISHED, diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_nat.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_nat.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_nat.h Sat Aug 3 02:39:45 2002 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_nat.h Fri Nov 29 00:53:15 2002 @@ -60,22 +60,6 @@ struct ip_nat_range range[1]; }; -#ifdef __KERNEL__ -#include -#include - -/* Protects NAT hash tables, and NAT-private part of conntracks. */ -DECLARE_RWLOCK_EXTERN(ip_nat_lock); - -/* Hashes for by-source and IP/protocol. */ -struct ip_nat_hash -{ - struct list_head list; - - /* conntrack we're embedded in: NULL if not in hash. */ - struct ip_conntrack *conntrack; -}; - /* Worst case: local-out manip + 1 post-routing, and reverse dirn. */ #define IP_NAT_MAX_MANIPS (2*3) @@ -93,7 +77,23 @@ /* Manipulations to occur at each conntrack in this dirn. */ struct ip_conntrack_manip manip; }; - + +#ifdef __KERNEL__ +#include +#include + +/* Protects NAT hash tables, and NAT-private part of conntracks. */ +DECLARE_RWLOCK_EXTERN(ip_nat_lock); + +/* Hashes for by-source and IP/protocol. */ +struct ip_nat_hash +{ + struct list_head list; + + /* conntrack we're embedded in: NULL if not in hash. */ + struct ip_conntrack *conntrack; +}; + /* The structure embedded in the conntrack structure. */ struct ip_nat_info { diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_nat_helper.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_nat_helper.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_nat_helper.h Thu Apr 26 00:00:28 2001 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_nat_helper.h Fri Nov 29 00:53:15 2002 @@ -6,23 +6,37 @@ struct sk_buff; +/* Flags */ +/* NAT helper must be called on every packet (for TCP) */ +#define IP_NAT_HELPER_F_ALWAYS 0x01 +/* Standalone NAT helper, without a conntrack part */ +#define IP_NAT_HELPER_F_STANDALONE 0x02 + struct ip_nat_helper { - /* Internal use */ - struct list_head list; + struct list_head list; /* Internal use */ + const char *name; /* name of the module */ + unsigned char flags; /* Flags (see above) */ + struct module *me; /* pointer to self */ + /* Mask of things we will help: vs. tuple from server */ struct ip_conntrack_tuple tuple; struct ip_conntrack_tuple mask; /* Helper function: returns verdict */ unsigned int (*help)(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, struct ip_nat_info *info, enum ip_conntrack_info ctinfo, unsigned int hooknum, struct sk_buff **pskb); - const char *name; + /* Returns verdict and sets up NAT for this connection */ + unsigned int (*expect)(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info); }; extern struct list_head helpers; @@ -39,5 +53,5 @@ extern int ip_nat_seq_adjust(struct sk_buff *skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo); -extern void ip_nat_delete_sack(struct sk_buff *skb, struct tcphdr *tcph); +extern void ip_nat_delete_sack(struct sk_buff *skb); #endif diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_nat_rule.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_nat_rule.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ip_nat_rule.h Mon Dec 11 22:31:32 2000 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ip_nat_rule.h Fri Nov 29 00:53:15 2002 @@ -5,24 +5,7 @@ #include #ifdef __KERNEL__ -/* Want to be told when we first NAT an expected packet for a conntrack? */ -struct ip_nat_expect -{ - struct list_head list; - /* Returns 1 (and sets verdict) if it has setup NAT for this - connection */ - int (*expect)(struct sk_buff **pskb, - unsigned int hooknum, - struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, - unsigned int *verdict); -}; - -extern int ip_nat_expect_register(struct ip_nat_expect *expect); -extern void ip_nat_expect_unregister(struct ip_nat_expect *expect); extern int ip_nat_rule_init(void) __init; extern void ip_nat_rule_cleanup(void); extern int ip_nat_rule_find(struct sk_buff **pskb, diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_DSCP.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_DSCP.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_DSCP.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_DSCP.h Fri Nov 29 00:53:15 2002 @@ -0,0 +1,20 @@ +/* iptables module for setting the IPv4 DSCP field + * + * (C) 2002 Harald Welte + * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh + * This software is distributed under GNU GPL v2, 1991 + * + * See RFC2474 for a description of the DSCP field within the IP Header. + * + * ipt_DSCP.h,v 1.7 2002/03/14 12:03:13 laforge Exp +*/ +#ifndef _IPT_DSCP_TARGET_H +#define _IPT_DSCP_TARGET_H +#include + +/* target info */ +struct ipt_DSCP_info { + u_int8_t dscp; +}; + +#endif /* _IPT_DSCP_TARGET_H */ diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_ECN.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_ECN.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_ECN.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_ECN.h Fri Nov 29 00:53:15 2002 @@ -0,0 +1,31 @@ +/* Header file for iptables ipt_ECN target + * + * (C) 2002 by Harald Welte + * + * This software is distributed under GNU GPL v2, 1991 + * + * ipt_ECN.h,v 1.3 2002/05/29 12:17:40 laforge Exp +*/ +#ifndef _IPT_ECN_TARGET_H +#define _IPT_ECN_TARGET_H +#include + +#define IPT_ECN_IP_MASK (~IPT_DSCP_MASK) + +#define IPT_ECN_OP_SET_IP 0x01 /* set ECN bits of IPv4 header */ +#define IPT_ECN_OP_SET_ECE 0x10 /* set ECE bit of TCP header */ +#define IPT_ECN_OP_SET_CWR 0x20 /* set CWR bit of TCP header */ + +#define IPT_ECN_OP_MASK 0xce + +struct ipt_ECN_info { + u_int8_t operation; /* bitset of operations */ + u_int8_t ip_ect; /* ECT codepoint of IPv4 header, pre-shifted */ + union { + struct { + u_int8_t ece:1, cwr:1; /* TCP ECT bits */ + } tcp; + } proto; +}; + +#endif /* _IPT_ECN_TARGET_H */ diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_conntrack.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_conntrack.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_conntrack.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_conntrack.h Fri Nov 29 00:53:15 2002 @@ -0,0 +1,38 @@ +/* Header file for kernel module to match connection tracking information. + * GPL (C) 2001 Marc Boucher (marc@mbsi.ca). + */ + +#ifndef _IPT_CONNTRACK_H +#define _IPT_CONNTRACK_H + +#define IPT_CONNTRACK_STATE_BIT(ctinfo) (1 << ((ctinfo)%IP_CT_IS_REPLY+1)) +#define IPT_CONNTRACK_STATE_INVALID (1 << 0) + +#define IPT_CONNTRACK_STATE_SNAT (1 << (IP_CT_NUMBER + 1)) +#define IPT_CONNTRACK_STATE_DNAT (1 << (IP_CT_NUMBER + 2)) + +/* flags, invflags: */ +#define IPT_CONNTRACK_STATE 0x01 +#define IPT_CONNTRACK_PROTO 0x02 +#define IPT_CONNTRACK_ORIGSRC 0x04 +#define IPT_CONNTRACK_ORIGDST 0x08 +#define IPT_CONNTRACK_REPLSRC 0x10 +#define IPT_CONNTRACK_REPLDST 0x20 +#define IPT_CONNTRACK_STATUS 0x40 +#define IPT_CONNTRACK_EXPIRES 0x80 + +struct ipt_conntrack_info +{ + unsigned int statemask, statusmask; + + struct ip_conntrack_tuple tuple[IP_CT_DIR_MAX]; + struct in_addr sipmsk[IP_CT_DIR_MAX], dipmsk[IP_CT_DIR_MAX]; + + unsigned long expires_min, expires_max; + + /* Flags word */ + u_int8_t flags; + /* Inverse flags */ + u_int8_t invflags; +}; +#endif /*_IPT_CONNTRACK_H*/ diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_dscp.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_dscp.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_dscp.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_dscp.h Fri Nov 29 00:53:15 2002 @@ -0,0 +1,23 @@ +/* iptables module for matching the IPv4 DSCP field + * + * (C) 2002 Harald Welte + * This software is distributed under GNU GPL v2, 1991 + * + * See RFC2474 for a description of the DSCP field within the IP Header. + * + * ipt_dscp.h,v 1.3 2002/08/05 19:00:21 laforge Exp +*/ +#ifndef _IPT_DSCP_H +#define _IPT_DSCP_H + +#define IPT_DSCP_MASK 0xfc /* 11111100 */ +#define IPT_DSCP_SHIFT 2 +#define IPT_DSCP_MAX 0x3f /* 00111111 */ + +/* match info */ +struct ipt_dscp_info { + u_int8_t dscp; + u_int8_t invert; +}; + +#endif /* _IPT_DSCP_H */ diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_ecn.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_ecn.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_ecn.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_ecn.h Fri Nov 29 00:53:15 2002 @@ -0,0 +1,33 @@ +/* iptables module for matching the ECN header in IPv4 and TCP header + * + * (C) 2002 Harald Welte + * + * This software is distributed under GNU GPL v2, 1991 + * + * ipt_ecn.h,v 1.4 2002/08/05 19:39:00 laforge Exp +*/ +#ifndef _IPT_ECN_H +#define _IPT_ECN_H +#include + +#define IPT_ECN_IP_MASK (~IPT_DSCP_MASK) + +#define IPT_ECN_OP_MATCH_IP 0x01 +#define IPT_ECN_OP_MATCH_ECE 0x10 +#define IPT_ECN_OP_MATCH_CWR 0x20 + +#define IPT_ECN_OP_MATCH_MASK 0xce + +/* match info */ +struct ipt_ecn_info { + u_int8_t operation; + u_int8_t invert; + u_int8_t ip_ect; + union { + struct { + u_int8_t ect; + } tcp; + } proto; +}; + +#endif /* _IPT_ECN_H */ diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_helper.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_helper.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_helper.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_helper.h Fri Nov 29 00:53:15 2002 @@ -0,0 +1,8 @@ +#ifndef _IPT_HELPER_H +#define _IPT_HELPER_H + +struct ipt_helper_info { + int invert; + char name[30]; +}; +#endif /* _IPT_HELPER_H */ diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_owner.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_owner.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_owner.h Fri Mar 17 19:56:20 2000 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_owner.h Fri Nov 29 00:53:15 2002 @@ -6,12 +6,14 @@ #define IPT_OWNER_GID 0x02 #define IPT_OWNER_PID 0x04 #define IPT_OWNER_SID 0x08 +#define IPT_OWNER_COMM 0x10 struct ipt_owner_info { uid_t uid; gid_t gid; pid_t pid; pid_t sid; + char comm[16]; u_int8_t match, invert; /* flags */ }; diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_pkttype.h linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_pkttype.h --- linux-2.4.19-plain/include/linux/netfilter_ipv4/ipt_pkttype.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/include/linux/netfilter_ipv4/ipt_pkttype.h Fri Nov 29 00:53:15 2002 @@ -0,0 +1,8 @@ +#ifndef _IPT_PKTTYPE_H +#define _IPT_PKTTYPE_H + +struct ipt_pkttype_info { + int pkttype; + int invert; +}; +#endif /*_IPT_PKTTYPE_H*/ diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/include/linux/netfilter_ipv6/ip6t_length.h linux-2.4.20-plain/include/linux/netfilter_ipv6/ip6t_length.h --- linux-2.4.19-plain/include/linux/netfilter_ipv6/ip6t_length.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/include/linux/netfilter_ipv6/ip6t_length.h Fri Nov 29 00:53:15 2002 @@ -0,0 +1,10 @@ +#ifndef _IP6T_LENGTH_H +#define _IP6T_LENGTH_H + +struct ip6t_length_info { + u_int16_t min, max; + u_int8_t invert; +}; + +#endif /*_IP6T_LENGTH_H*/ + diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/Config.in linux-2.4.20-plain/net/ipv4/netfilter/Config.in --- linux-2.4.19-plain/net/ipv4/netfilter/Config.in Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/Config.in Fri Nov 29 00:53:15 2002 @@ -18,15 +18,24 @@ # The simple matches. dep_tristate ' limit match support' CONFIG_IP_NF_MATCH_LIMIT $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 ' TOS match support' CONFIG_IP_NF_MATCH_TOS $CONFIG_IP_NF_IPTABLES + dep_tristate ' ECN match support' CONFIG_IP_NF_MATCH_ECN $CONFIG_IP_NF_IPTABLES + + dep_tristate ' DSCP match support' CONFIG_IP_NF_MATCH_DSCP $CONFIG_IP_NF_IPTABLES + dep_tristate ' AH/ESP match support' CONFIG_IP_NF_MATCH_AH_ESP $CONFIG_IP_NF_IPTABLES dep_tristate ' LENGTH match support' CONFIG_IP_NF_MATCH_LENGTH $CONFIG_IP_NF_IPTABLES dep_tristate ' TTL match support' CONFIG_IP_NF_MATCH_TTL $CONFIG_IP_NF_IPTABLES dep_tristate ' tcpmss match support' CONFIG_IP_NF_MATCH_TCPMSS $CONFIG_IP_NF_IPTABLES if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then + dep_tristate ' Helper match support' CONFIG_IP_NF_MATCH_HELPER $CONFIG_IP_NF_IPTABLES + fi + 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 ' 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 @@ -73,6 +82,10 @@ dep_tristate ' Packet mangling' CONFIG_IP_NF_MANGLE $CONFIG_IP_NF_IPTABLES if [ "$CONFIG_IP_NF_MANGLE" != "n" ]; then dep_tristate ' TOS target support' CONFIG_IP_NF_TARGET_TOS $CONFIG_IP_NF_MANGLE + dep_tristate ' ECN target support' CONFIG_IP_NF_TARGET_ECN $CONFIG_IP_NF_MANGLE + + dep_tristate ' DSCP target support' CONFIG_IP_NF_TARGET_DSCP $CONFIG_IP_NF_MANGLE + dep_tristate ' MARK target support' CONFIG_IP_NF_TARGET_MARK $CONFIG_IP_NF_MANGLE fi dep_tristate ' LOG target support' CONFIG_IP_NF_TARGET_LOG $CONFIG_IP_NF_IPTABLES diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/Makefile linux-2.4.20-plain/net/ipv4/netfilter/Makefile --- linux-2.4.19-plain/net/ipv4/netfilter/Makefile Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/Makefile Fri Nov 29 00:53:15 2002 @@ -9,18 +9,18 @@ O_TARGET := netfilter.o -export-objs = ip_conntrack_standalone.o ip_conntrack_ftp.o ip_fw_compat.o ip_nat_standalone.o ip_tables.o arp_tables.o +export-objs = ip_conntrack_standalone.o ip_fw_compat.o ip_nat_standalone.o ip_tables.o arp_tables.o # Multipart objects. list-multi := ip_conntrack.o iptable_nat.o ipfwadm.o ipchains.o # objects for the conntrack and NAT core (used by standalone and backw. compat) ip_nf_conntrack-objs := ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o -ip_nf_nat-objs := ip_nat_core.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o +ip_nf_nat-objs := ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o # objects for the standalone - connection tracking / NAT ip_conntrack-objs := ip_conntrack_standalone.o $(ip_nf_conntrack-objs) -iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o ip_nat_helper.o $(ip_nf_nat-objs) +iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o $(ip_nf_nat-objs) # objects for backwards compatibility mode ip_nf_compat-objs := ip_fw_compat.o ip_fw_compat_redir.o ip_fw_compat_masq.o $(ip_nf_conntrack-objs) $(ip_nf_nat-objs) @@ -33,7 +33,14 @@ # connection tracking helpers obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o +ifdef CONFIG_IP_NF_NAT_FTP + export-objs += ip_conntrack_ftp.o +endif + obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o +ifdef CONFIG_IP_NF_NAT_IRC + export-objs += ip_conntrack_irc.o +endif # NAT helpers obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o @@ -48,18 +55,24 @@ obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o # matches +obj-$(CONFIG_IP_NF_MATCH_HELPER) += ipt_helper.o obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o obj-$(CONFIG_IP_NF_MATCH_MARK) += ipt_mark.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_OWNER) += ipt_owner.o obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o +obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o +obj-$(CONFIG_IP_NF_MATCH_DSCP) += ipt_dscp.o obj-$(CONFIG_IP_NF_MATCH_AH_ESP) += ipt_ah.o ipt_esp.o obj-$(CONFIG_IP_NF_MATCH_LENGTH) += ipt_length.o obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o +obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o @@ -67,6 +80,8 @@ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o +obj-$(CONFIG_IP_NF_TARGET_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 diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_core.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_core.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_core.c Fri Nov 29 00:53:15 2002 @@ -3,7 +3,15 @@ extension. */ /* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General - Public Licence. */ + * Public Licence. + * + * 23 Apr 2001: Harald Welte + * - new API and handling of conntrack/nat helpers + * - now capable of multiple expectations for one master + * 16 Jul 2002: Harald Welte + * - add usage/reference counts to ip_conntrack_expect + * - export ip_conntrack[_expect]_{find_get,put} functions + * */ #ifdef MODULE #define __NO_VERSION__ @@ -37,6 +45,8 @@ #include #include +#define IP_CONNTRACK_VERSION "2.1" + #if 0 #define DEBUGP printk #else @@ -44,9 +54,10 @@ #endif DECLARE_RWLOCK(ip_conntrack_lock); +DECLARE_RWLOCK(ip_conntrack_expect_tuple_lock); void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL; -LIST_HEAD(expect_list); +LIST_HEAD(ip_conntrack_expect_list); LIST_HEAD(protocol_list); static LIST_HEAD(helpers); unsigned int ip_conntrack_htable_size = 0; @@ -63,7 +74,7 @@ return protocol == curr->proto; } -struct ip_conntrack_protocol *__find_proto(u_int8_t protocol) +struct ip_conntrack_protocol *__ip_ct_find_proto(u_int8_t protocol) { struct ip_conntrack_protocol *p; @@ -76,17 +87,18 @@ return p; } -struct ip_conntrack_protocol *find_proto(u_int8_t protocol) +struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol) { struct ip_conntrack_protocol *p; READ_LOCK(&ip_conntrack_lock); - p = __find_proto(protocol); + p = __ip_ct_find_proto(protocol); READ_UNLOCK(&ip_conntrack_lock); return p; } -static inline void ip_conntrack_put(struct ip_conntrack *ct) +inline void +ip_conntrack_put(struct ip_conntrack *ct) { IP_NF_ASSERT(ct); IP_NF_ASSERT(ct->infos[0].master); @@ -150,9 +162,135 @@ return protocol->invert_tuple(inverse, orig); } + +/* ip_conntrack_expect helper functions */ + +/* Compare tuple parts depending on mask. */ +static inline int expect_cmp(const struct ip_conntrack_expect *i, + const struct ip_conntrack_tuple *tuple) +{ + MUST_BE_READ_LOCKED(&ip_conntrack_expect_tuple_lock); + return ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask); +} + +static void +destroy_expect(struct ip_conntrack_expect *exp) +{ + DEBUGP("destroy_expect(%p) use=%d\n", exp, atomic_read(exp->use)); + IP_NF_ASSERT(atomic_read(exp->use)); + IP_NF_ASSERT(!timer_pending(&exp->timeout)); + + kfree(exp); +} + + +inline void ip_conntrack_expect_put(struct ip_conntrack_expect *exp) +{ + IP_NF_ASSERT(exp); + + if (atomic_dec_and_test(&exp->use)) { + /* usage count dropped to zero */ + destroy_expect(exp); + } +} + +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); + return LIST_FIND(&ip_conntrack_expect_list, expect_cmp, + struct ip_conntrack_expect *, tuple); +} + +/* Find a expectation corresponding to a tuple. */ +struct ip_conntrack_expect * +ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple) +{ + struct ip_conntrack_expect *exp; + + READ_LOCK(&ip_conntrack_lock); + READ_LOCK(&ip_conntrack_expect_tuple_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); + + return exp; +} + +/* remove one specific expectation from all lists and drop refcount, + * does _NOT_ delete the timer. */ +static 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--; + + ip_conntrack_expect_put(expect); +} + +/* 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) +{ + 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); +} + +/* delete all unconfirmed expectations for this conntrack */ +static void remove_expectations(struct ip_conntrack *ct) +{ + struct list_head *exp_entry, *next; + struct ip_conntrack_expect *exp; + + DEBUGP("remove_expectations(%p)\n", ct); + + for (exp_entry = ct->sibling_list.next; + exp_entry != &ct->sibling_list; exp_entry = next) { + next = exp_entry->next; + exp = list_entry(exp_entry, struct ip_conntrack_expect, + expected_list); + + /* we skip established expectations, as we want to delete + * the un-established ones only */ + if (exp->sibling) { + DEBUGP("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); + } +} + static void clean_from_lists(struct ip_conntrack *ct) { + DEBUGP("clean_from_lists(%p)\n", ct); MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); /* Remove from both hash lists: must not NULL out next ptrs, otherwise we'll look unconfirmed. Fortunately, LIST_DELETE @@ -163,12 +301,9 @@ LIST_DELETE(&ip_conntrack_hash [hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple)], &ct->tuplehash[IP_CT_DIR_REPLY]); - /* If our expected is in the list, take it out. */ - if (ct->expected.expectant) { - IP_NF_ASSERT(list_inlist(&expect_list, &ct->expected)); - IP_NF_ASSERT(ct->expected.expectant == ct); - LIST_DELETE(&expect_list, &ct->expected); - } + + /* Destroy all un-established, pending expectations */ + remove_expectations(ct); } static void @@ -177,21 +312,34 @@ struct ip_conntrack *ct = (struct ip_conntrack *)nfct; struct ip_conntrack_protocol *proto; + DEBUGP("destroy_conntrack(%p)\n", ct); IP_NF_ASSERT(atomic_read(&nfct->use) == 0); IP_NF_ASSERT(!timer_pending(&ct->timeout)); - if (ct->master.master) - nf_conntrack_put(&ct->master); + if (ct->master && master_ct(ct)) + ip_conntrack_put(master_ct(ct)); /* To make sure we don't get any weird locking issues here: * destroy_conntrack() MUST NOT be called with a write lock * to ip_conntrack_lock!!! -HW */ - proto = find_proto(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum); + proto = ip_ct_find_proto(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum); if (proto && proto->destroy) proto->destroy(ct); if (ip_conntrack_destroyed) ip_conntrack_destroyed(ct); + + WRITE_LOCK(&ip_conntrack_lock); + /* Delete our master expectation */ + if (ct->master) { + /* can't call __unexpect_related here, + * since it would screw up expect_list */ + list_del(&ct->master->expected_list); + kfree(ct->master); + } + WRITE_UNLOCK(&ip_conntrack_lock); + + DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct); kmem_cache_free(ip_conntrack_cachep, ct); atomic_dec(&ip_conntrack_count); } @@ -315,7 +463,7 @@ &ct->tuplehash[IP_CT_DIR_REPLY]); /* Timer relative to confirmation time, not original setting time, otherwise we'd get timer wrap in - wierd delay cases. */ + weird delay cases. */ ct->timeout.expires += jiffies; add_timer(&ct->timeout); atomic_inc(&ct->ct_general.use); @@ -389,7 +537,7 @@ return NULL; } - innerproto = find_proto(inner->protocol); + innerproto = ip_ct_find_proto(inner->protocol); /* Are they talking about one of our connections? */ if (inner->ihl * 4 + 8 > datalen || !get_tuple(inner, datalen, &origtuple, innerproto)) { @@ -469,11 +617,11 @@ return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask); } -/* Compare parts depending on mask. */ -static inline int expect_cmp(const struct ip_conntrack_expect *i, - const struct ip_conntrack_tuple *tuple) +struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple) { - return ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask); + return LIST_FIND(&helpers, helper_cmp, + struct ip_conntrack_helper *, + tuple); } /* Allocate a new conntrack: we return -ENOMEM if classification @@ -521,7 +669,7 @@ return ERR_PTR(-ENOMEM); } - memset(conntrack, 0, sizeof(struct ip_conntrack)); + memset(conntrack, 0, sizeof(*conntrack)); atomic_set(&conntrack->ct_general.use, 1); conntrack->ct_general.destroy = destroy_conntrack; conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple; @@ -540,31 +688,44 @@ conntrack->timeout.data = (unsigned long)conntrack; conntrack->timeout.function = death_by_timeout; + INIT_LIST_HEAD(&conntrack->sibling_list); + /* Mark clearly that it's not in the hash table. */ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list.next = NULL; - /* Write lock required for deletion of expected. Without - this, a read-lock would do. */ WRITE_LOCK(&ip_conntrack_lock); - conntrack->helper = LIST_FIND(&helpers, helper_cmp, - struct ip_conntrack_helper *, - &repl_tuple); /* Need finding and deleting of expected ONLY if we win race */ - expected = LIST_FIND(&expect_list, expect_cmp, + 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); + + /* Look up the conntrack helper for master connections only */ + if (!expected) + conntrack->helper = ip_ct_find_helper(&repl_tuple); + + /* If the expectation is dying, then this is a looser. */ + if (expected + && expected->expectant->helper->timeout + && ! del_timer(&expected->timeout)) + expected = NULL; + /* If master is not in hash table yet (ie. packet hasn't left this machine yet), how can other end know about expected? Hence these are not the droids you are looking for (if master ct never got confirmed, we'd hold a reference to it and weird things would happen to future packets). */ if (expected && is_confirmed(expected->expectant)) { + DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", + conntrack, expected); /* Welcome, Mr. Bond. We've been expecting you... */ + IP_NF_ASSERT(master_ct(conntrack)); conntrack->status = IPS_EXPECTED; - conntrack->master.master = &expected->expectant->ct_general; - IP_NF_ASSERT(conntrack->master.master); - LIST_DELETE(&expect_list, expected); - expected->expectant = NULL; - nf_conntrack_get(&conntrack->master); + conntrack->master = expected; + expected->sibling = conntrack; + LIST_DELETE(&ip_conntrack_expect_list, expected); + expected->expectant->expecting--; + nf_conntrack_get(&master_ct(conntrack)->infos[0]); } atomic_inc(&ip_conntrack_count); WRITE_UNLOCK(&ip_conntrack_lock); @@ -669,7 +830,7 @@ return NF_STOLEN; } - proto = find_proto((*pskb)->nh.iph->protocol); + proto = ip_ct_find_proto((*pskb)->nh.iph->protocol); /* It may be an icmp error... */ if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP @@ -713,66 +874,229 @@ int invert_tuplepr(struct ip_conntrack_tuple *inverse, const struct ip_conntrack_tuple *orig) { - return invert_tuple(inverse, orig, find_proto(orig->dst.protonum)); + return invert_tuple(inverse, orig, ip_ct_find_proto(orig->dst.protonum)); } -static void unexpect_related(struct ip_conntrack *related_to) -{ - MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); - list_del(&related_to->expected.list); - related_to->expected.expectant = NULL; +static inline int resent_expect(const struct ip_conntrack_expect *i, + const struct ip_conntrack_tuple *tuple, + const struct ip_conntrack_tuple *mask) +{ + DEBUGP("resent_expect\n"); + DEBUGP(" tuple: "); DUMP_TUPLE(&i->tuple); + DEBUGP("ct_tuple: "); DUMP_TUPLE(&i->ct_tuple); + DEBUGP("test tuple: "); DUMP_TUPLE(tuple); + return (((i->ct_tuple.dst.protonum == 0 && ip_ct_tuple_equal(&i->tuple, tuple)) + || (i->ct_tuple.dst.protonum && ip_ct_tuple_equal(&i->ct_tuple, tuple))) + && ip_ct_tuple_equal(&i->mask, mask)); } /* Would two expected things clash? */ static inline int expect_clash(const struct ip_conntrack_expect *i, - const struct ip_conntrack_expect *new) + const struct ip_conntrack_tuple *tuple, + const struct ip_conntrack_tuple *mask) { /* Part covered by intersection of masks must be unequal, otherwise they clash */ struct ip_conntrack_tuple intersect_mask - = { { i->mask.src.ip & new->mask.src.ip, - { i->mask.src.u.all & new->mask.src.u.all } }, - { i->mask.dst.ip & new->mask.dst.ip, - { i->mask.dst.u.all & new->mask.dst.u.all }, - i->mask.dst.protonum & new->mask.dst.protonum } }; + = { { i->mask.src.ip & mask->src.ip, + { i->mask.src.u.all & mask->src.u.all } }, + { i->mask.dst.ip & mask->dst.ip, + { i->mask.dst.u.all & mask->dst.u.all }, + i->mask.dst.protonum & mask->dst.protonum } }; + + return ip_ct_tuple_mask_cmp(&i->tuple, tuple, &intersect_mask); +} + +inline void ip_conntrack_unexpect_related(struct ip_conntrack_expect *expect) +{ + WRITE_LOCK(&ip_conntrack_lock); + unexpect_related(expect); + WRITE_UNLOCK(&ip_conntrack_lock); +} + +static void expectation_timed_out(unsigned long ul_expect) +{ + struct ip_conntrack_expect *expect = (void *) ul_expect; - return ip_ct_tuple_mask_cmp(&i->tuple, &new->tuple, &intersect_mask); + DEBUGP("expectation %p timed out\n", expect); + WRITE_LOCK(&ip_conntrack_lock); + __unexpect_related(expect); + WRITE_UNLOCK(&ip_conntrack_lock); } /* Add a related connection. */ int ip_conntrack_expect_related(struct ip_conntrack *related_to, - const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *mask, - int (*expectfn)(struct ip_conntrack *)) + struct ip_conntrack_expect *expect) { + struct ip_conntrack_expect *old, *new; + int ret = 0; + WRITE_LOCK(&ip_conntrack_lock); - if (related_to->expected.expectant) - unexpect_related(related_to); + /* Because of the write lock, no reader can walk the lists, + * so there is no need to use the tuple lock too */ + + 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); + 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); + return -EEXIST; + } + } else if (related_to->helper->max_expected && + related_to->expecting >= related_to->helper->max_expected) { + struct list_head *cur_item; + /* old == NULL */ + if (net_ratelimit()) + printk(KERN_WARNING + "ip_conntrack: max number of expected " + "connections %i of %s reached for " + "%u.%u.%u.%u->%u.%u.%u.%u%s\n", + related_to->helper->max_expected, + related_to->helper->name, + NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), + NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip), + related_to->helper->flags & IP_CT_HELPER_F_REUSE_EXPECT ? + ", reusing" : ""); + if (!(related_to->helper->flags & + IP_CT_HELPER_F_REUSE_EXPECT)) { + WRITE_UNLOCK(&ip_conntrack_lock); + return -EPERM; + } - related_to->expected.tuple = *tuple; - related_to->expected.mask = *mask; - related_to->expected.expectfn = expectfn; + /* choose the the oldest expectation to evict */ + list_for_each(cur_item, &related_to->sibling_list) { + struct ip_conntrack_expect *cur; + + cur = list_entry(cur_item, + struct ip_conntrack_expect, + expected_list); + if (cur->sibling == NULL) { + old = cur; + break; + } + } - if (LIST_FIND(&expect_list, expect_clash, - struct ip_conntrack_expect *, &related_to->expected)) { + /* (!old) cannot happen, since related_to->expecting is the + * number of unconfirmed expects */ + IP_NF_ASSERT(old); + + /* 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. + */ + unexpect_related(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); + DEBUGP("expect_related: busy!\n"); return -EBUSY; } + + 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"); + return -ENOMEM; + } + + /* Zero out the new structure, then fill out it with the data */ + DEBUGP("new expectation %p of conntrack %p\n", new, related_to); + memset(new, 0, sizeof(*expect)); + INIT_LIST_HEAD(&new->list); + INIT_LIST_HEAD(&new->expected_list); + memcpy(new, expect, sizeof(*expect)); + new->expectant = related_to; + new->sibling = NULL; + /* increase usage count. This sucks. The memset above overwrites + * old usage count [if still present] and we increase to one. Only + * works because everything is done under ip_conntrack_lock() */ + atomic_inc(&new->use); + + /* add to expected list for this connection */ + 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_prepend(&expect_list, &related_to->expected); - related_to->expected.expectant = related_to; WRITE_UNLOCK(&ip_conntrack_lock); - return 0; + return ret; } -void ip_conntrack_unexpect_related(struct ip_conntrack *related_to) +/* Change tuple in an existing expectation */ +int ip_conntrack_change_expect(struct ip_conntrack_expect *expect, + struct ip_conntrack_tuple *newtuple) { - WRITE_LOCK(&ip_conntrack_lock); - unexpect_related(related_to); - WRITE_UNLOCK(&ip_conntrack_lock); -} + int ret; + + MUST_BE_READ_LOCKED(&ip_conntrack_lock); + WRITE_LOCK(&ip_conntrack_expect_tuple_lock); + + DEBUGP("change_expect:\n"); + DEBUGP("exp tuple: "); DUMP_TUPLE(&expect->tuple); + DEBUGP("exp mask: "); DUMP_TUPLE(&expect->mask); + DEBUGP("newtuple: "); DUMP_TUPLE(newtuple); + if (expect->ct_tuple.dst.protonum == 0) { + /* Never seen before */ + DEBUGP("change expect: never seen before\n"); + if (!ip_ct_tuple_equal(&expect->tuple, newtuple) + && LIST_FIND(&ip_conntrack_expect_list, expect_clash, + struct ip_conntrack_expect *, newtuple, &expect->mask)) { + /* Force NAT to find an unused tuple */ + ret = -1; + } else { + memcpy(&expect->ct_tuple, &expect->tuple, sizeof(expect->tuple)); + memcpy(&expect->tuple, newtuple, sizeof(expect->tuple)); + ret = 0; + } + } else { + /* Resent packet */ + DEBUGP("change expect: resent packet\n"); + if (ip_ct_tuple_equal(&expect->tuple, newtuple)) { + ret = 0; + } else { + /* Force NAT to choose again the same port */ + ret = -1; + } + } + WRITE_UNLOCK(&ip_conntrack_expect_tuple_lock); + return ret; +} + /* Alter reply tuple (maybe alter helper). If it's already taken, return 0 and don't do alteration. */ int ip_conntrack_alter_reply(struct ip_conntrack *conntrack, @@ -790,10 +1114,12 @@ DUMP_TUPLE(newreply); conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; - conntrack->helper = LIST_FIND(&helpers, helper_cmp, - struct ip_conntrack_helper *, - newreply); + if (!conntrack->master) + conntrack->helper = LIST_FIND(&helpers, helper_cmp, + struct ip_conntrack_helper *, + newreply); WRITE_UNLOCK(&ip_conntrack_lock); + return 1; } @@ -812,14 +1138,10 @@ const struct ip_conntrack_helper *me) { if (i->ctrack->helper == me) { - i->ctrack->helper = NULL; /* Get rid of any expected. */ - if (i->ctrack->expected.expectant) { - IP_NF_ASSERT(i->ctrack->expected.expectant - == i->ctrack); - LIST_DELETE(&expect_list, &i->ctrack->expected); - i->ctrack->expected.expectant = NULL; - } + remove_expectations(i->ctrack); + /* And *then* set helper to NULL */ + i->ctrack->helper = NULL; } return 0; } @@ -1100,18 +1422,22 @@ } ip_conntrack_max = 8 * ip_conntrack_htable_size; - printk("ip_conntrack (%u buckets, %d max)\n", - ip_conntrack_htable_size, ip_conntrack_max); + printk("ip_conntrack version %s (%u buckets, %d max)" + " - %d bytes per conntrack\n", IP_CONNTRACK_VERSION, + ip_conntrack_htable_size, ip_conntrack_max, + sizeof(struct ip_conntrack)); ret = nf_register_sockopt(&so_getorigdst); - if (ret != 0) + if (ret != 0) { + printk(KERN_ERR "Unable to register netfilter socket option\n"); return ret; + } ip_conntrack_hash = vmalloc(sizeof(struct list_head) * ip_conntrack_htable_size); if (!ip_conntrack_hash) { - nf_unregister_sockopt(&so_getorigdst); - return -ENOMEM; + printk(KERN_ERR "Unable to create ip_conntrack_hash\n"); + goto err_unreg_sockopt; } ip_conntrack_cachep = kmem_cache_create("ip_conntrack", @@ -1119,11 +1445,8 @@ SLAB_HWCACHE_ALIGN, NULL, NULL); if (!ip_conntrack_cachep) { printk(KERN_ERR "Unable to create ip_conntrack slab cache\n"); - vfree(ip_conntrack_hash); - nf_unregister_sockopt(&so_getorigdst); - return -ENOMEM; + goto err_free_hash; } - /* Don't NEED lock here, but good form anyway. */ WRITE_LOCK(&ip_conntrack_lock); /* Sew in builtin protocols. */ @@ -1142,14 +1465,20 @@ ip_conntrack_sysctl_header = register_sysctl_table(ip_conntrack_root_table, 0); if (ip_conntrack_sysctl_header == NULL) { - kmem_cache_destroy(ip_conntrack_cachep); - vfree(ip_conntrack_hash); - nf_unregister_sockopt(&so_getorigdst); - return -ENOMEM; + goto err_free_ct_cachep; } #endif /*CONFIG_SYSCTL*/ /* For use by ipt_REJECT */ ip_ct_attach = ip_conntrack_attach; return ret; + +err_free_ct_cachep: + kmem_cache_destroy(ip_conntrack_cachep); +err_free_hash: + vfree(ip_conntrack_hash); +err_unreg_sockopt: + nf_unregister_sockopt(&so_getorigdst); + + return -ENOMEM; } diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_ftp.c linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_ftp.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_ftp.c Wed Oct 31 00:08:12 2001 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_ftp.c Fri Nov 29 00:53:15 2002 @@ -1,4 +1,5 @@ /* FTP extension for IP connection tracking. */ +#include #include #include #include @@ -15,7 +16,7 @@ #define MAX_PORTS 8 static int ports[MAX_PORTS]; -static int ports_c; +static int ports_c = 0; #ifdef MODULE_PARM MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); #endif @@ -242,8 +243,10 @@ u_int32_t array[6] = { 0 }; int dir = CTINFO2DIR(ctinfo); unsigned int matchlen, matchoff; - struct ip_conntrack_tuple t, mask; - struct ip_ct_ftp *info = &ct->help.ct_ftp_info; + struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_ftp_expect *exp_ftp_info = &exp->help.exp_ftp_info; + unsigned int i; int found = 0; @@ -271,8 +274,8 @@ } LOCK_BH(&ip_ftp_lock); - old_seq_aft_nl_set = info->seq_aft_nl_set[dir]; - old_seq_aft_nl = info->seq_aft_nl[dir]; + old_seq_aft_nl_set = ct_ftp_info->seq_aft_nl_set[dir]; + old_seq_aft_nl = ct_ftp_info->seq_aft_nl[dir]; DEBUGP("conntrack_ftp: datalen %u\n", datalen); if ((datalen > 0) && (data[datalen-1] == '\n')) { @@ -281,8 +284,9 @@ || after(ntohl(tcph->seq) + datalen, old_seq_aft_nl)) { DEBUGP("conntrack_ftp: updating nl to %u\n", ntohl(tcph->seq) + datalen); - info->seq_aft_nl[dir] = ntohl(tcph->seq) + datalen; - info->seq_aft_nl_set[dir] = 1; + ct_ftp_info->seq_aft_nl[dir] = + ntohl(tcph->seq) + datalen; + ct_ftp_info->seq_aft_nl_set[dir] = 1; } } UNLOCK_BH(&ip_ftp_lock); @@ -330,16 +334,17 @@ DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n", (int)matchlen, data + matchoff, matchlen, ntohl(tcph->seq) + matchoff); + + memset(&expect, 0, sizeof(expect)); /* Update the ftp info */ LOCK_BH(&ip_ftp_lock); if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]) == ct->tuplehash[dir].tuple.src.ip) { - info->is_ftp = 21; - info->seq = ntohl(tcph->seq) + matchoff; - info->len = matchlen; - info->ftptype = search[i].ftptype; - info->port = array[4] << 8 | array[5]; + exp->seq = ntohl(tcph->seq) + matchoff; + exp_ftp_info->len = matchlen; + exp_ftp_info->ftptype = search[i].ftptype; + exp_ftp_info->port = array[4] << 8 | array[5]; } else { /* Enrico Scholz's passive FTP to partially RNAT'd ftp server: it really wants us to connect to a @@ -356,18 +361,21 @@ if (!loose) goto out; } - t = ((struct ip_conntrack_tuple) + exp->tuple = ((struct ip_conntrack_tuple) { { ct->tuplehash[!dir].tuple.src.ip, { 0 } }, { htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]), { htons(array[4] << 8 | array[5]) }, IPPROTO_TCP }}); - mask = ((struct ip_conntrack_tuple) + exp->mask = ((struct ip_conntrack_tuple) { { 0xFFFFFFFF, { 0 } }, { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; + /* Ignore failure; should only happen with NAT */ - ip_conntrack_expect_related(ct, &t, &mask, NULL); + ip_conntrack_expect_related(ct, &expect); out: UNLOCK_BH(&ip_ftp_lock); @@ -375,12 +383,13 @@ } static struct ip_conntrack_helper ftp[MAX_PORTS]; +static char ftp_names[MAX_PORTS][10]; /* Not __exit: called from init() */ static void fini(void) { int i; - for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + for (i = 0; i < ports_c; i++) { DEBUGP("ip_ct_ftp: unregistering helper for port %d\n", ports[i]); ip_conntrack_helper_unregister(&ftp[i]); @@ -390,9 +399,10 @@ static int __init init(void) { int i, ret; + char *tmpname; if (ports[0] == 0) - ports[0] = 21; + ports[0] = FTP_PORT; for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { memset(&ftp[i], 0, sizeof(struct ip_conntrack_helper)); @@ -400,7 +410,19 @@ ftp[i].tuple.dst.protonum = IPPROTO_TCP; ftp[i].mask.src.u.tcp.port = 0xFFFF; ftp[i].mask.dst.protonum = 0xFFFF; + ftp[i].max_expected = 1; + ftp[i].timeout = 0; + ftp[i].flags = IP_CT_HELPER_F_REUSE_EXPECT; + ftp[i].me = ip_conntrack_ftp; ftp[i].help = help; + + tmpname = &ftp_names[i][0]; + if (ports[i] == FTP_PORT) + sprintf(tmpname, "ftp"); + else + sprintf(tmpname, "ftp-%d", ports[i]); + ftp[i].name = tmpname; + DEBUGP("ip_ct_ftp: registering helper for port %d\n", ports[i]); ret = ip_conntrack_helper_register(&ftp[i]); @@ -414,10 +436,10 @@ return 0; } - +#ifdef CONFIG_IP_NF_NAT_NEEDED EXPORT_SYMBOL(ip_ftp_lock); -EXPORT_SYMBOL(ip_conntrack_ftp); -MODULE_LICENSE("GPL"); +#endif +MODULE_LICENSE("GPL"); module_init(init); module_exit(fini); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_irc.c linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_irc.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_irc.c Mon Feb 25 20:38:14 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_irc.c Fri Nov 29 00:53:15 2002 @@ -11,12 +11,18 @@ ** * Module load syntax: * insmod ip_conntrack_irc.o ports=port1,port2,...port + * max_dcc_channels=n dcc_timeout=secs * * please give the ports of all IRC servers You wish to connect to. - * If You don't specify ports, the default will be port 6667 + * If You don't specify ports, the default will be port 6667. + * With max_dcc_channels you can define the maximum number of not + * yet answered DCC channels per IRC session (default 8). + * With dcc_timeout you can specify how long the system waits for + * an expected DCC channel (default 300 seconds). * */ +#include #include #include #include @@ -29,7 +35,9 @@ #define MAX_PORTS 8 static int ports[MAX_PORTS]; -static int ports_n_c = 0; +static int ports_c = 0; +static int max_dcc_channels = 8; +static unsigned int dcc_timeout = 300; MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("IRC (DCC) connection tracking module"); @@ -37,6 +45,10 @@ #ifdef MODULE_PARM MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); MODULE_PARM_DESC(ports, "port numbers of IRC servers"); +MODULE_PARM(max_dcc_channels, "i"); +MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per IRC session"); +MODULE_PARM(dcc_timeout, "i"); +MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels"); #endif #define NUM_DCCPROTO 5 @@ -103,23 +115,15 @@ u_int32_t tcplen = len - iph->ihl * 4; u_int32_t datalen = tcplen - tcph->doff * 4; int dir = CTINFO2DIR(ctinfo); - struct ip_conntrack_tuple t, mask; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_irc_expect *exp_irc_info = &exp->help.exp_irc_info; u_int32_t dcc_ip; u_int16_t dcc_port; int i; char *addr_beg_p, *addr_end_p; - struct ip_ct_irc *info = &ct->help.ct_irc_info; - - mask = ((struct ip_conntrack_tuple) - { { 0, { 0 } }, - { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); - DEBUGP("entered\n"); - /* Can't track connections formed before we registered */ - if (!info) - return NF_ACCEPT; /* If packet is coming from IRC server */ if (dir == IP_CT_DIR_REPLY) @@ -189,33 +193,37 @@ continue; } + + memset(&expect, 0, sizeof(expect)); LOCK_BH(&ip_irc_lock); /* save position of address in dcc string, * neccessary for NAT */ - info->is_irc = IP_CONNTR_IRC; DEBUGP("tcph->seq = %u\n", tcph->seq); - info->seq = ntohl(tcph->seq) + (addr_beg_p - _data); - info->len = (addr_end_p - addr_beg_p); - info->port = dcc_port; + exp->seq = ntohl(tcph->seq) + (addr_beg_p - _data); + exp_irc_info->len = (addr_end_p - addr_beg_p); + exp_irc_info->port = dcc_port; DEBUGP("wrote info seq=%u (ofs=%u), len=%d\n", - info->seq, (addr_end_p - _data), info->len); + exp->seq, (addr_end_p - _data), exp_irc_info->len); - memset(&t, 0, sizeof(t)); - t.src.ip = 0; - t.src.u.tcp.port = 0; - t.dst.ip = htonl(dcc_ip); - t.dst.u.tcp.port = htons(info->port); - t.dst.protonum = IPPROTO_TCP; + exp->tuple = ((struct ip_conntrack_tuple) + { { 0, { 0 } }, + { htonl(dcc_ip), { htons(dcc_port) }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", - NIPQUAD(t.src.ip), - ntohs(t.src.u.tcp.port), - NIPQUAD(t.dst.ip), - ntohs(t.dst.u.tcp.port)); + NIPQUAD(exp->tuple.src.ip), + ntohs(exp->tuple.src.u.tcp.port), + NIPQUAD(exp->tuple.dst.ip), + ntohs(exp->tuple.dst.u.tcp.port)); - ip_conntrack_expect_related(ct, &t, &mask, NULL); + ip_conntrack_expect_related(ct, &expect); UNLOCK_BH(&ip_irc_lock); return NF_ACCEPT; @@ -226,29 +234,53 @@ } static struct ip_conntrack_helper irc_helpers[MAX_PORTS]; +static char irc_names[MAX_PORTS][10]; static void fini(void); static int __init init(void) { int i, ret; + struct ip_conntrack_helper *hlpr; + char *tmpname; + if (max_dcc_channels < 1) { + printk("ip_conntrack_irc: max_dcc_channels must be a positive integer\n"); + return -EBUSY; + } + if (dcc_timeout < 0) { + printk("ip_conntrack_irc: dcc_timeout must be a positive integer\n"); + return -EBUSY; + } + /* If no port given, default to standard irc port */ if (ports[0] == 0) - ports[0] = 6667; + ports[0] = IRC_PORT; for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { - memset(&irc_helpers[i], 0, + hlpr = &irc_helpers[i]; + memset(hlpr, 0, sizeof(struct ip_conntrack_helper)); - irc_helpers[i].tuple.src.u.tcp.port = htons(ports[i]); - irc_helpers[i].tuple.dst.protonum = IPPROTO_TCP; - irc_helpers[i].mask.src.u.tcp.port = 0xFFFF; - irc_helpers[i].mask.dst.protonum = 0xFFFF; - irc_helpers[i].help = help; + hlpr->tuple.src.u.tcp.port = htons(ports[i]); + hlpr->tuple.dst.protonum = IPPROTO_TCP; + hlpr->mask.src.u.tcp.port = 0xFFFF; + hlpr->mask.dst.protonum = 0xFFFF; + hlpr->max_expected = max_dcc_channels; + hlpr->timeout = dcc_timeout; + hlpr->flags = IP_CT_HELPER_F_REUSE_EXPECT; + hlpr->me = ip_conntrack_irc; + hlpr->help = help; + + tmpname = &irc_names[i][0]; + if (ports[i] == IRC_PORT) + sprintf(tmpname, "irc"); + else + sprintf(tmpname, "irc-%d", i); + hlpr->name = tmpname; DEBUGP("port #%d: %d\n", i, ports[i]); - ret = ip_conntrack_helper_register(&irc_helpers[i]); + ret = ip_conntrack_helper_register(hlpr); if (ret) { printk("ip_conntrack_irc: ERROR registering port %d\n", @@ -256,7 +288,7 @@ fini(); return -EBUSY; } - ports_n_c++; + ports_c++; } return 0; } @@ -266,12 +298,16 @@ static void fini(void) { int i; - for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + for (i = 0; i < ports_c; i++) { DEBUGP("unregistering port %d\n", ports[i]); ip_conntrack_helper_unregister(&irc_helpers[i]); } } + +#ifdef CONFIG_IP_NF_NAT_NEEDED +EXPORT_SYMBOL(ip_irc_lock); +#endif module_init(init); module_exit(fini); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_proto_generic.c linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_proto_generic.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_proto_generic.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_proto_generic.c Fri Nov 29 00:53:15 2002 @@ -57,5 +57,5 @@ struct ip_conntrack_protocol ip_conntrack_generic_protocol = { { NULL, NULL }, 0, "unknown", generic_pkt_to_tuple, generic_invert_tuple, generic_print_tuple, - generic_print_conntrack, established, new, NULL, NULL }; + generic_print_conntrack, established, new, NULL, NULL, NULL }; diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_proto_icmp.c linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_proto_icmp.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_proto_icmp.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_proto_icmp.c Fri Nov 29 00:53:15 2002 @@ -113,4 +113,4 @@ struct ip_conntrack_protocol ip_conntrack_protocol_icmp = { { NULL, NULL }, IPPROTO_ICMP, "icmp", icmp_pkt_to_tuple, icmp_invert_tuple, icmp_print_tuple, - icmp_print_conntrack, icmp_packet, icmp_new, NULL, NULL }; + icmp_print_conntrack, icmp_packet, icmp_new, NULL, NULL, NULL }; diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_proto_tcp.c linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_proto_tcp.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_proto_tcp.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_proto_tcp.c Fri Nov 29 00:53:15 2002 @@ -7,6 +7,10 @@ #include #include #include +#include + +#include + #include #include #include @@ -227,7 +231,19 @@ return 1; } +static int tcp_exp_matches_pkt(struct ip_conntrack_expect *exp, + struct sk_buff **pskb) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); + unsigned int datalen; + + datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4; + + return between(exp->seq, ntohl(tcph->seq), ntohl(tcph->seq) + datalen); +} + struct ip_conntrack_protocol ip_conntrack_protocol_tcp = { { NULL, NULL }, IPPROTO_TCP, "tcp", tcp_pkt_to_tuple, tcp_invert_tuple, tcp_print_tuple, tcp_print_conntrack, - tcp_packet, tcp_new, NULL, NULL }; + tcp_packet, tcp_new, NULL, tcp_exp_matches_pkt, NULL }; diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_proto_udp.c linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_proto_udp.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_proto_udp.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_proto_udp.c Fri Nov 29 00:53:15 2002 @@ -71,4 +71,4 @@ struct ip_conntrack_protocol ip_conntrack_protocol_udp = { { NULL, NULL }, IPPROTO_UDP, "udp", udp_pkt_to_tuple, udp_invert_tuple, udp_print_tuple, udp_print_conntrack, - udp_packet, udp_new, NULL, NULL }; + udp_packet, udp_new, NULL, NULL, NULL }; diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_standalone.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_conntrack_standalone.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_conntrack_standalone.c Fri Nov 29 00:53:15 2002 @@ -62,10 +62,16 @@ { unsigned int len; - len = sprintf(buffer, "EXPECTING: proto=%u ", - expect->tuple.dst.protonum); + if (expect->expectant->helper->timeout) + len = sprintf(buffer, "EXPECTING: %lu ", + timer_pending(&expect->timeout) + ? (expect->timeout.expires - jiffies)/HZ : 0); + else + len = sprintf(buffer, "EXPECTING: - "); + len += sprintf(buffer + len, "use=%u proto=%u ", + atomic_read(&expect->use), expect->tuple.dst.protonum); len += print_tuple(buffer + len, &expect->tuple, - __find_proto(expect->tuple.dst.protonum)); + __ip_ct_find_proto(expect->tuple.dst.protonum)); len += sprintf(buffer + len, "\n"); return len; } @@ -75,7 +81,7 @@ { unsigned int len; struct ip_conntrack_protocol *proto - = __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 ", @@ -147,7 +153,8 @@ } /* Now iterate through expecteds. */ - for (e = expect_list.next; e != &expect_list; e = e->next) { + for (e = ip_conntrack_expect_list.next; + e != &ip_conntrack_expect_list; e = e->next) { unsigned int last_len; struct ip_conntrack_expect *expect = (struct ip_conntrack_expect *)e; @@ -314,7 +321,7 @@ { WRITE_LOCK(&ip_conntrack_lock); - /* find_proto() returns proto_generic in case there is no protocol + /* ip_ct_find_proto() returns proto_generic in case there is no protocol * helper. So this should be enough - HW */ LIST_DELETE(&protocol_list, proto); WRITE_UNLOCK(&ip_conntrack_lock); @@ -353,8 +360,19 @@ EXPORT_SYMBOL(ip_conntrack_helper_unregister); EXPORT_SYMBOL(ip_ct_selective_cleanup); EXPORT_SYMBOL(ip_ct_refresh); +EXPORT_SYMBOL(ip_ct_find_proto); +EXPORT_SYMBOL(__ip_ct_find_proto); +EXPORT_SYMBOL(ip_ct_find_helper); EXPORT_SYMBOL(ip_conntrack_expect_related); +EXPORT_SYMBOL(ip_conntrack_change_expect); EXPORT_SYMBOL(ip_conntrack_unexpect_related); +EXPORT_SYMBOL_GPL(ip_conntrack_expect_find_get); +EXPORT_SYMBOL_GPL(ip_conntrack_expect_put); EXPORT_SYMBOL(ip_conntrack_tuple_taken); EXPORT_SYMBOL(ip_ct_gather_frags); EXPORT_SYMBOL(ip_conntrack_htable_size); +EXPORT_SYMBOL(ip_conntrack_expect_list); +EXPORT_SYMBOL(ip_conntrack_lock); +EXPORT_SYMBOL(ip_conntrack_hash); +EXPORT_SYMBOL_GPL(ip_conntrack_find_get); +EXPORT_SYMBOL_GPL(ip_conntrack_put); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_fw_compat_masq.c linux-2.4.20-plain/net/ipv4/netfilter/ip_fw_compat_masq.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_fw_compat_masq.c Mon Feb 25 20:38:14 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_fw_compat_masq.c Fri Nov 29 00:53:15 2002 @@ -130,7 +130,7 @@ struct ip_conntrack *ct; int ret; - protocol = find_proto(iph->protocol); + protocol = ip_ct_find_proto(iph->protocol); /* We don't feed packets to conntrack system unless we know they're part of an connection already established by an diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_core.c linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_core.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_core.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_core.c Fri Nov 29 00:53:15 2002 @@ -21,10 +21,14 @@ #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock) #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock) +#include +#include +#include #include #include #include #include +#include #include #if 0 @@ -34,6 +38,7 @@ #endif DECLARE_RWLOCK(ip_nat_lock); +DECLARE_RWLOCK_EXTERN(ip_conntrack_lock); /* Calculated at init based on memory size */ static unsigned int ip_nat_htable_size; @@ -198,6 +203,7 @@ return NULL; } +#ifdef CONFIG_IP_NF_NAT_LOCAL /* If it's really a local destination manip, it may need to do a source manip too. */ static int @@ -216,6 +222,7 @@ ip_rt_put(rt); return 1; } +#endif /* Simple way to iterate through all. */ static inline int fake_cmp(const struct ip_nat_hash *i, @@ -628,8 +635,9 @@ } /* If there's a helper, assign it; based on new tuple. */ - info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, - &reply); + if (!conntrack->master) + info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, + &reply); /* It's done. */ info->initialized |= (1 << HOOK2MANIP(hooknum)); @@ -724,6 +732,20 @@ #endif } +static inline int exp_for_packet(struct ip_conntrack_expect *exp, + struct sk_buff **pskb) +{ + struct ip_conntrack_protocol *proto; + int ret = 1; + + MUST_BE_READ_LOCKED(&ip_conntrack_lock); + proto = __ip_ct_find_proto((*pskb)->nh.iph->protocol); + if (proto->exp_matches_pkt) + ret = proto->exp_matches_pkt(exp, pskb); + + return ret; +} + /* Do packet manipulations according to binding. */ unsigned int do_bindings(struct ip_conntrack *ct, @@ -735,6 +757,7 @@ unsigned int i; struct ip_nat_helper *helper; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + int is_tcp = (*pskb)->nh.iph->protocol == IPPROTO_TCP; /* Need nat lock to protect against modification, but neither conntrack (referenced) and helper (deleted with @@ -773,11 +796,66 @@ READ_UNLOCK(&ip_nat_lock); if (helper) { + struct ip_conntrack_expect *exp = NULL; + struct list_head *cur_item; + int ret = NF_ACCEPT; + + DEBUGP("do_bindings: helper existing for (%p)\n", ct); + /* Always defragged for helpers */ IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off - & __constant_htons(IP_MF|IP_OFFSET))); - return helper->help(ct, info, ctinfo, hooknum, pskb); - } else return NF_ACCEPT; + & htons(IP_MF|IP_OFFSET))); + + /* Have to grab read lock before sibling_list traversal */ + READ_LOCK(&ip_conntrack_lock); + list_for_each(cur_item, &ct->sibling_list) { + exp = list_entry(cur_item, struct ip_conntrack_expect, + expected_list); + + /* if this expectation is already established, skip */ + if (exp->sibling) + continue; + + if (exp_for_packet(exp, pskb)) { + + DEBUGP("calling nat helper (exp=%p) for packet\n", + exp); + ret = helper->help(ct, exp, info, ctinfo, + hooknum, pskb); + if (ret != NF_ACCEPT) { + READ_UNLOCK(&ip_conntrack_lock); + return ret; + } + } + } + /* Helper might want to manip the packet even when there is no expectation */ + if (!exp && helper->flags & IP_NAT_HELPER_F_ALWAYS) { + DEBUGP("calling nat helper for packet without expectation\n"); + ret = helper->help(ct, NULL, info, ctinfo, + hooknum, pskb); + if (ret != NF_ACCEPT) { + READ_UNLOCK(&ip_conntrack_lock); + return ret; + } + } + READ_UNLOCK(&ip_conntrack_lock); + + /* Adjust sequence number only once per packet + * (helper is called at all hooks) */ + if (is_tcp && (hooknum == NF_IP_POST_ROUTING + || hooknum == NF_IP_LOCAL_IN)) { + DEBUGP("ip_nat_core: adjusting sequence number\n"); + /* future: put this in a l4-proto specific function, + * and call this function here. */ + ip_nat_seq_adjust(*pskb, ct, ctinfo); + } + + return ret; + + } else + return NF_ACCEPT; + + /* not reached */ } unsigned int @@ -816,7 +894,7 @@ -static int +static unsigned int ftp_nat_expected(struct sk_buff **pskb, unsigned int hooknum, struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, - unsigned int *verdict) + struct ip_nat_info *info) { struct ip_nat_multi_range mr; u_int32_t newdstip, newsrcip, newip; - struct ip_ct_ftp *ftpinfo; + struct ip_ct_ftp_expect *exp_ftp_info; + struct ip_conntrack *master = master_ct(ct); + IP_NF_ASSERT(info); IP_NF_ASSERT(master); - IP_NF_ASSERT(masterinfo); IP_NF_ASSERT(!(info->initialized & (1<help.ct_ftp_info; + exp_ftp_info = &ct->master->help.exp_ftp_info; LOCK_BH(&ip_ftp_lock); - if (ftpinfo->is_ftp != 21) { - UNLOCK_BH(&ip_ftp_lock); - DEBUGP("nat_expected: master not ftp\n"); - return 0; - } - if (ftpinfo->ftptype == IP_CT_FTP_PORT - || ftpinfo->ftptype == IP_CT_FTP_EPRT) { + if (exp_ftp_info->ftptype == IP_CT_FTP_PORT + || exp_ftp_info->ftptype == IP_CT_FTP_EPRT) { /* PORT command: make connection go to the client. */ newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; @@ -92,11 +84,9 @@ mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; mr.range[0].min = mr.range[0].max = ((union ip_conntrack_manip_proto) - { htons(ftpinfo->port) }); + { htons(exp_ftp_info->port) }); } - *verdict = ip_nat_setup_info(ct, &mr, hooknum); - - return 1; + return ip_nat_setup_info(ct, &mr, hooknum); } static int @@ -176,27 +166,22 @@ [IP_CT_FTP_EPSV] mangle_epsv_packet }; -static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info, +static int ftp_data_fixup(const struct ip_ct_ftp_expect *ct_ftp_info, struct ip_conntrack *ct, - unsigned int datalen, struct sk_buff **pskb, - enum ip_conntrack_info ctinfo) + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) { u_int32_t newip; struct iphdr *iph = (*pskb)->nh.iph; struct tcphdr *tcph = (void *)iph + iph->ihl*4; u_int16_t port; - struct ip_conntrack_tuple tuple; - /* Don't care about source port */ - const struct ip_conntrack_tuple mask - = { { 0xFFFFFFFF, { 0 } }, - { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF } }; + struct ip_conntrack_tuple newtuple; - memset(&tuple, 0, sizeof(tuple)); MUST_BE_LOCKED(&ip_ftp_lock); - DEBUGP("FTP_NAT: seq %u + %u in %u + %u\n", - ct_ftp_info->seq, ct_ftp_info->len, - ntohl(tcph->seq), datalen); + DEBUGP("FTP_NAT: seq %u + %u in %u\n", + expect->seq, ct_ftp_info->len, + ntohl(tcph->seq)); /* Change address inside packet to match way we're mapping this connection. */ @@ -206,29 +191,34 @@ is */ newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; /* Expect something from client->server */ - tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; - tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + newtuple.src.ip = + ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newtuple.dst.ip = + ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; } else { /* PORT command: must be where server thinks client is */ newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; /* Expect something from server->client */ - tuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; - tuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + newtuple.src.ip = + ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newtuple.dst.ip = + ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; } - tuple.dst.protonum = IPPROTO_TCP; + newtuple.dst.protonum = IPPROTO_TCP; + newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port; /* Try to get same port: if not, try to change it. */ for (port = ct_ftp_info->port; port != 0; port++) { - tuple.dst.u.tcp.port = htons(port); + newtuple.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(ct, &tuple, &mask, NULL) == 0) + if (ip_conntrack_change_expect(expect, &newtuple) == 0) break; } if (port == 0) return 0; if (!mangle[ct_ftp_info->ftptype](pskb, newip, port, - ct_ftp_info->seq - ntohl(tcph->seq), + expect->seq - ntohl(tcph->seq), ct_ftp_info->len, ct, ctinfo)) return 0; @@ -236,6 +226,7 @@ } static unsigned int help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, struct ip_nat_info *info, enum ip_conntrack_info ctinfo, unsigned int hooknum, @@ -245,13 +236,12 @@ struct tcphdr *tcph = (void *)iph + iph->ihl*4; unsigned int datalen; int dir; - int score; - struct ip_ct_ftp *ct_ftp_info - = &ct->help.ct_ftp_info; - - /* Delete SACK_OK on initial TCP SYNs. */ - if (tcph->syn && !tcph->ack) - ip_nat_delete_sack(*pskb, tcph); + struct ip_ct_ftp_expect *ct_ftp_info; + + if (!exp) + DEBUGP("ip_nat_ftp: no exp!!"); + + ct_ftp_info = &exp->help.exp_ftp_info; /* Only mangle things once: original direction in POST_ROUTING and reply direction on PRE_ROUTING. */ @@ -267,103 +257,87 @@ } datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; - score = 0; LOCK_BH(&ip_ftp_lock); - if (ct_ftp_info->len) { - /* If it's in the right range... */ - score += between(ct_ftp_info->seq, ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - score += between(ct_ftp_info->seq + ct_ftp_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - if (score == 1) { - /* Half a match? This means a partial retransmisison. - It's a cracker being funky. */ - if (net_ratelimit()) { - printk("FTP_NAT: partial packet %u/%u in %u/%u\n", - ct_ftp_info->seq, ct_ftp_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - } + /* If it's in the right range... */ + if (between(exp->seq + ct_ftp_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + if (!ftp_data_fixup(ct_ftp_info, ct, pskb, ctinfo, exp)) { UNLOCK_BH(&ip_ftp_lock); return NF_DROP; - } else if (score == 2) { - if (!ftp_data_fixup(ct_ftp_info, ct, datalen, - pskb, ctinfo)) { - UNLOCK_BH(&ip_ftp_lock); - return NF_DROP; - } - /* skb may have been reallocated */ - iph = (*pskb)->nh.iph; - tcph = (void *)iph + iph->ihl*4; } + } else { + /* Half a match? This means a partial retransmisison. + It's a cracker being funky. */ + if (net_ratelimit()) { + printk("FTP_NAT: partial packet %u/%u in %u/%u\n", + exp->seq, ct_ftp_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + UNLOCK_BH(&ip_ftp_lock); + return NF_DROP; } - UNLOCK_BH(&ip_ftp_lock); - ip_nat_seq_adjust(*pskb, ct, ctinfo); - return NF_ACCEPT; } static struct ip_nat_helper ftp[MAX_PORTS]; -static char ftp_names[MAX_PORTS][6]; - -static struct ip_nat_expect ftp_expect -= { { NULL, NULL }, ftp_nat_expected }; +static char ftp_names[MAX_PORTS][10]; /* Not __exit: called from init() */ static void fini(void) { int i; - for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + for (i = 0; i < ports_c; i++) { DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]); ip_nat_helper_unregister(&ftp[i]); } - - ip_nat_expect_unregister(&ftp_expect); } static int __init init(void) { - int i, ret; + int i, ret = 0; char *tmpname; - ret = ip_nat_expect_register(&ftp_expect); - if (ret == 0) { - if (ports[0] == 0) - ports[0] = 21; - - for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { - - memset(&ftp[i], 0, sizeof(struct ip_nat_helper)); - - ftp[i].tuple.dst.protonum = IPPROTO_TCP; - ftp[i].tuple.src.u.tcp.port = htons(ports[i]); - ftp[i].mask.dst.protonum = 0xFFFF; - ftp[i].mask.src.u.tcp.port = 0xFFFF; - ftp[i].help = help; - - tmpname = &ftp_names[i][0]; - sprintf(tmpname, "ftp%2.2d", i); - ftp[i].name = tmpname; - - DEBUGP("ip_nat_ftp: Trying to register for port %d\n", - ports[i]); - ret = ip_nat_helper_register(&ftp[i]); - - if (ret) { - printk("ip_nat_ftp: error registering helper for port %d\n", ports[i]); - fini(); - return ret; - } - ports_c++; - } + if (ports[0] == 0) + ports[0] = FTP_PORT; - } else { - ip_nat_expect_unregister(&ftp_expect); + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + + memset(&ftp[i], 0, sizeof(struct ip_nat_helper)); + + ftp[i].tuple.dst.protonum = IPPROTO_TCP; + ftp[i].tuple.src.u.tcp.port = htons(ports[i]); + ftp[i].mask.dst.protonum = 0xFFFF; + ftp[i].mask.src.u.tcp.port = 0xFFFF; + ftp[i].help = help; + ftp[i].me = THIS_MODULE; + ftp[i].flags = 0; + ftp[i].expect = ftp_nat_expected; + + tmpname = &ftp_names[i][0]; + if (ports[i] == FTP_PORT) + sprintf(tmpname, "ftp"); + else + sprintf(tmpname, "ftp-%d", i); + ftp[i].name = tmpname; + + DEBUGP("ip_nat_ftp: Trying to register for port %d\n", + ports[i]); + ret = ip_nat_helper_register(&ftp[i]); + + if (ret) { + printk("ip_nat_ftp: error registering " + "helper for port %d\n", ports[i]); + fini(); + return ret; + } + ports_c++; } + return ret; } diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_helper.c linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_helper.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_helper.c Fri Dec 21 18:42:05 2001 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_helper.c Fri Nov 29 00:53:15 2002 @@ -1,11 +1,18 @@ /* ip_nat_mangle.c - generic support functions for NAT helpers * - * (C) 2000 by Harald Welte + * (C) 2000-2002 by Harald Welte * * distributed under the terms of GNU GPL + * + * 14 Jan 2002 Harald Welte : + * - add support for SACK adjustment + * 14 Mar 2002 Harald Welte : + * - merge SACK support into newnat API */ #include +#include #include +#include #include #include #include @@ -19,6 +26,8 @@ #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock) #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock) +#include +#include #include #include #include @@ -32,7 +41,7 @@ #define DEBUGP(format, args...) #define DUMP_OFFSET(x) #endif - + DECLARE_LOCK(ip_nat_seqofs_lock); static inline int @@ -199,6 +208,103 @@ return 1; } +/* Adjust one found SACK option including checksum correction */ +static void +sack_adjust(struct tcphdr *tcph, + unsigned char *ptr, + struct ip_nat_seq *natseq) +{ + struct tcp_sack_block *sp = (struct tcp_sack_block *)(ptr+2); + int num_sacks = (ptr[1] - TCPOLEN_SACK_BASE)>>3; + int i; + + for (i = 0; i < num_sacks; i++, sp++) { + u_int32_t new_start_seq, new_end_seq; + + if (after(ntohl(sp->start_seq) - natseq->offset_before, + natseq->correction_pos)) + new_start_seq = ntohl(sp->start_seq) + - natseq->offset_after; + else + new_start_seq = ntohl(sp->start_seq) + - natseq->offset_before; + new_start_seq = htonl(new_start_seq); + + if (after(ntohl(sp->end_seq) - natseq->offset_before, + natseq->correction_pos)) + new_end_seq = ntohl(sp->end_seq) + - natseq->offset_after; + else + new_end_seq = ntohl(sp->end_seq) + - natseq->offset_before; + new_end_seq = htonl(new_end_seq); + + DEBUGP("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", + ntohl(sp->start_seq), new_start_seq, + ntohl(sp->end_seq), new_end_seq); + + tcph->check = + ip_nat_cheat_check(~sp->start_seq, new_start_seq, + ip_nat_cheat_check(~sp->end_seq, + new_end_seq, + tcph->check)); + + sp->start_seq = new_start_seq; + sp->end_seq = new_end_seq; + } +} + + +/* TCP SACK sequence number adjustment, return 0 if sack found and adjusted */ +static inline int +ip_nat_sack_adjust(struct sk_buff *skb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct iphdr *iph; + struct tcphdr *tcph; + unsigned char *ptr; + int length, dir, sack_adjusted = 0; + + iph = skb->nh.iph; + tcph = (void *)iph + iph->ihl*4; + length = (tcph->doff*4)-sizeof(struct tcphdr); + ptr = (unsigned char *)(tcph+1); + + dir = CTINFO2DIR(ctinfo); + + while (length > 0) { + int opcode = *ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return !sack_adjusted; + case TCPOPT_NOP: + length--; + continue; + default: + opsize = *ptr++; + if (opsize > length) /* no partial opts */ + return !sack_adjusted; + if (opcode == TCPOPT_SACK) { + /* found SACK */ + if((opsize >= (TCPOLEN_SACK_BASE + +TCPOLEN_SACK_PERBLOCK)) && + !((opsize - TCPOLEN_SACK_BASE) + % TCPOLEN_SACK_PERBLOCK)) + sack_adjust(tcph, ptr-2, + &ct->nat.info.seq[!dir]); + + sack_adjusted = 1; + } + ptr += opsize-2; + length -= opsize; + } + } + return !sack_adjusted; +} + /* TCP sequence number adjustment */ int ip_nat_seq_adjust(struct sk_buff *skb, @@ -243,51 +349,9 @@ tcph->seq = newseq; tcph->ack_seq = newack; - return 0; -} - -/* Grrr... SACK. Fuck me even harder. Don't want to fix it on the - fly, so blow it away. */ -void -ip_nat_delete_sack(struct sk_buff *skb, struct tcphdr *tcph) -{ - unsigned int i; - u_int8_t *opt = (u_int8_t *)tcph; - - DEBUGP("Seeking SACKPERM in SYN packet (doff = %u).\n", - tcph->doff * 4); - for (i = sizeof(struct tcphdr); i < tcph->doff * 4;) { - DEBUGP("%u ", opt[i]); - switch (opt[i]) { - case TCPOPT_NOP: - case TCPOPT_EOL: - i++; - break; + ip_nat_sack_adjust(skb, ct, ctinfo); - case TCPOPT_SACK_PERM: - goto found_opt; - - default: - /* Worst that can happen: it will take us over. */ - i += opt[i+1] ?: 1; - } - } - DEBUGP("\n"); - return; - - found_opt: - DEBUGP("\n"); - DEBUGP("Found SACKPERM at offset %u.\n", i); - - /* Must be within TCP header, and valid SACK perm. */ - if (i + opt[i+1] <= tcph->doff*4 && opt[i+1] == 2) { - /* Replace with NOPs. */ - tcph->check - = ip_nat_cheat_check(*((u_int16_t *)(opt + i))^0xFFFF, - (TCPOPT_NOP<<8)|TCPOPT_NOP, tcph->check); - opt[i] = opt[i+1] = TCPOPT_NOP; - } - else DEBUGP("Something wrong with SACK_PERM.\n"); + return 0; } static inline int @@ -297,10 +361,51 @@ return ip_ct_tuple_mask_cmp(tuple, &helper->tuple, &helper->mask); } +#define MODULE_MAX_NAMELEN 32 + int ip_nat_helper_register(struct ip_nat_helper *me) { int ret = 0; + if (me->me && !(me->flags & IP_NAT_HELPER_F_STANDALONE)) { + struct ip_conntrack_helper *ct_helper; + + if ((ct_helper = ip_ct_find_helper(&me->tuple)) + && ct_helper->me) { + __MOD_INC_USE_COUNT(ct_helper->me); + } else { + + /* We are a NAT helper for protocol X. If we need + * respective conntrack helper for protoccol X, compute + * conntrack helper name and try to load module */ + char name[MODULE_MAX_NAMELEN]; + const char *tmp = me->me->name; + + if (strlen(tmp) + 6 > MODULE_MAX_NAMELEN) { + printk(__FUNCTION__ ": unable to " + "compute conntrack helper name " + "from %s\n", tmp); + return -EBUSY; + } + tmp += 6; + sprintf(name, "ip_conntrack%s", tmp); +#ifdef CONFIG_KMOD + if (!request_module(name) + && (ct_helper = ip_ct_find_helper(&me->tuple)) + && ct_helper->me) { + __MOD_INC_USE_COUNT(ct_helper->me); + } else { + printk("unable to load module %s\n", name); + return -EBUSY; + } +#else + printk("unable to load module %s automatically " + "because kernel was compiled without kernel " + "module loader support\n", name); + return -EBUSY; +#endif + } + } WRITE_LOCK(&ip_nat_lock); if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple)) ret = -EBUSY; @@ -327,8 +432,14 @@ void ip_nat_helper_unregister(struct ip_nat_helper *me) { + int found = 0; + WRITE_LOCK(&ip_nat_lock); - LIST_DELETE(&helpers, me); + /* Autoloading conntrack helper might have failed */ + if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple)) { + LIST_DELETE(&helpers, me); + found = 1; + } WRITE_UNLOCK(&ip_nat_lock); /* Someone could be still looking at the helper in a bh. */ @@ -344,5 +455,19 @@ worse. --RR */ ip_ct_selective_cleanup(kill_helper, me); - MOD_DEC_USE_COUNT; + if (found) + MOD_DEC_USE_COUNT; + + /* If we are no standalone NAT helper, we need to decrement usage count + * on our conntrack helper */ + if (me->me && !(me->flags & IP_NAT_HELPER_F_STANDALONE)) { + struct ip_conntrack_helper *ct_helper; + + if ((ct_helper = ip_ct_find_helper(&me->tuple)) + && ct_helper->me) { + __MOD_DEC_USE_COUNT(ct_helper->me); + } else + printk(__FUNCTION__ ": unable to decrement usage count" + " of conntrack helper %s\n", me->me->name); + } } diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_irc.c linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_irc.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_irc.c Fri Dec 21 18:42:05 2001 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_irc.c Fri Nov 29 00:53:15 2002 @@ -51,42 +51,29 @@ -static int +static unsigned int irc_nat_expected(struct sk_buff **pskb, unsigned int hooknum, struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, unsigned int *verdict) + struct ip_nat_info *info) { struct ip_nat_multi_range mr; u_int32_t newdstip, newsrcip, newip; - struct ip_ct_irc *ircinfo; + + struct ip_conntrack *master = master_ct(ct); IP_NF_ASSERT(info); IP_NF_ASSERT(master); - IP_NF_ASSERT(masterinfo); IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); DEBUGP("nat_expected: We have a connection!\n"); - /* Master must be an irc connection */ - ircinfo = &master->help.ct_irc_info; - LOCK_BH(&ip_irc_lock); - if (ircinfo->is_irc != IP_CONNTR_IRC) { - UNLOCK_BH(&ip_irc_lock); - DEBUGP("nat_expected: master not irc\n"); - return 0; - } - newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; DEBUGP("nat_expected: DCC cmd. %u.%u.%u.%u->%u.%u.%u.%u\n", NIPQUAD(newsrcip), NIPQUAD(newdstip)); - UNLOCK_BH(&ip_irc_lock); - if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) newip = newsrcip; else @@ -99,16 +86,14 @@ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; mr.range[0].min_ip = mr.range[0].max_ip = newip; - *verdict = ip_nat_setup_info(ct, &mr, hooknum); - - return 1; + return ip_nat_setup_info(ct, &mr, hooknum); } -static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info, +static int irc_data_fixup(const struct ip_ct_irc_expect *ct_irc_info, struct ip_conntrack *ct, - unsigned int datalen, struct sk_buff **pskb, - enum ip_conntrack_info ctinfo) + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) { u_int32_t newip; struct ip_conntrack_tuple t; @@ -121,9 +106,9 @@ MUST_BE_LOCKED(&ip_irc_lock); - DEBUGP("IRC_NAT: info (seq %u + %u) packet(seq %u + %u)\n", - ct_irc_info->seq, ct_irc_info->len, - ntohl(tcph->seq), datalen); + DEBUGP("IRC_NAT: info (seq %u + %u) in %u\n", + expect->seq, ct_irc_info->len, + ntohl(tcph->seq)); newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; @@ -133,13 +118,11 @@ only set in ip_conntrack_irc, with ip_irc_lock held writable */ - t = ct->expected.tuple; + t = expect->tuple; t.dst.ip = newip; for (port = ct_irc_info->port; port != 0; port++) { t.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(ct, &t, - &ct->expected.mask, - NULL) == 0) { + if (ip_conntrack_change_expect(expect, &t) == 0) { DEBUGP("using port %d", port); break; } @@ -166,26 +149,28 @@ buffer, NIPQUAD(newip), port); return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, - ct_irc_info->seq - ntohl(tcph->seq), + expect->seq - ntohl(tcph->seq), ct_irc_info->len, buffer, strlen(buffer)); } static unsigned int help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, struct ip_nat_info *info, enum ip_conntrack_info ctinfo, - unsigned int hooknum, struct sk_buff **pskb) + unsigned int hooknum, + struct sk_buff **pskb) { struct iphdr *iph = (*pskb)->nh.iph; struct tcphdr *tcph = (void *) iph + iph->ihl * 4; unsigned int datalen; int dir; - int score; - struct ip_ct_irc *ct_irc_info = &ct->help.ct_irc_info; + struct ip_ct_irc_expect *ct_irc_info; - /* Delete SACK_OK on initial TCP SYNs. */ - if (tcph->syn && !tcph->ack) - ip_nat_delete_sack(*pskb, tcph); + if (!exp) + DEBUGP("ip_nat_irc: no exp!!"); + + ct_irc_info = &exp->help.exp_irc_info; /* Only mangle things once: original direction in POST_ROUTING and reply direction on PRE_ROUTING. */ @@ -202,55 +187,35 @@ DEBUGP("got beyond not touching\n"); datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; - score = 0; LOCK_BH(&ip_irc_lock); - if (ct_irc_info->len) { - DEBUGP("got beyond ct_irc_info->len\n"); - - /* If it's in the right range... */ - score += between(ct_irc_info->seq, ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - score += between(ct_irc_info->seq + ct_irc_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - if (score == 1) { - /* Half a match? This means a partial retransmisison. - It's a cracker being funky. */ - if (net_ratelimit()) { - printk - ("IRC_NAT: partial packet %u/%u in %u/%u\n", - ct_irc_info->seq, ct_irc_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - } + /* Check wether the whole IP/address pattern is carried in the payload */ + if (between(exp->seq + ct_irc_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + if (!irc_data_fixup(ct_irc_info, ct, pskb, ctinfo, exp)) { UNLOCK_BH(&ip_irc_lock); return NF_DROP; - } else if (score == 2) { - DEBUGP("IRC_NAT: score=2, calling fixup\n"); - if (!irc_data_fixup(ct_irc_info, ct, datalen, - pskb, ctinfo)) { - UNLOCK_BH(&ip_irc_lock); - return NF_DROP; - } - /* skb may have been reallocated */ - iph = (*pskb)->nh.iph; - tcph = (void *) iph + iph->ihl * 4; } + } else { + /* Half a match? This means a partial retransmisison. + It's a cracker being funky. */ + if (net_ratelimit()) { + printk + ("IRC_NAT: partial packet %u/%u in %u/%u\n", + exp->seq, ct_irc_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + UNLOCK_BH(&ip_irc_lock); + return NF_DROP; } - UNLOCK_BH(&ip_irc_lock); - ip_nat_seq_adjust(*pskb, ct, ctinfo); - return NF_ACCEPT; } static struct ip_nat_helper ip_nat_irc_helpers[MAX_PORTS]; -static char ip_nih_names[MAX_PORTS][6]; - -static struct ip_nat_expect irc_expect - = { {NULL, NULL}, irc_nat_expected }; - +static char irc_names[MAX_PORTS][10]; /* This function is intentionally _NOT_ defined as __exit, because * it is needed by init() */ @@ -262,52 +227,54 @@ DEBUGP("ip_nat_irc: unregistering helper for port %d\n", ports[i]); ip_nat_helper_unregister(&ip_nat_irc_helpers[i]); - } - ip_nat_expect_unregister(&irc_expect); + } } + static int __init init(void) { - int ret; + int ret = 0; int i; struct ip_nat_helper *hlpr; char *tmpname; - ret = ip_nat_expect_register(&irc_expect); - if (ret == 0) { - - if (ports[0] == 0) { - ports[0] = 6667; - } + if (ports[0] == 0) { + ports[0] = IRC_PORT; + } - for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) { - hlpr = &ip_nat_irc_helpers[i]; - memset(hlpr, 0, - sizeof(struct ip_nat_helper)); - - hlpr->tuple.dst.protonum = IPPROTO_TCP; - hlpr->tuple.src.u.tcp.port = htons(ports[i]); - hlpr->mask.src.u.tcp.port = 0xFFFF; - hlpr->mask.dst.protonum = 0xFFFF; - hlpr->help = help; - - tmpname = &ip_nih_names[i][0]; - sprintf(tmpname, "irc%2.2d", i); - - hlpr->name = tmpname; - DEBUGP - ("ip_nat_irc: Trying to register helper for port %d: name %s\n", - ports[i], hlpr->name); - ret = ip_nat_helper_register(hlpr); - - if (ret) { - printk - ("ip_nat_irc: error registering helper for port %d\n", - ports[i]); - fini(); - return 1; - } - ports_c++; + for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) { + hlpr = &ip_nat_irc_helpers[i]; + memset(hlpr, 0, + sizeof(struct ip_nat_helper)); + + hlpr->tuple.dst.protonum = IPPROTO_TCP; + hlpr->tuple.src.u.tcp.port = htons(ports[i]); + hlpr->mask.src.u.tcp.port = 0xFFFF; + hlpr->mask.dst.protonum = 0xFFFF; + hlpr->help = help; + hlpr->flags = 0; + hlpr->me = THIS_MODULE; + hlpr->expect = irc_nat_expected; + + tmpname = &irc_names[i][0]; + if (ports[i] == IRC_PORT) + sprintf(tmpname, "irc"); + else + sprintf(tmpname, "irc-%d", i); + hlpr->name = tmpname; + + DEBUGP + ("ip_nat_irc: Trying to register helper for port %d: name %s\n", + ports[i], hlpr->name); + ret = ip_nat_helper_register(hlpr); + + if (ret) { + printk + ("ip_nat_irc: error registering helper for port %d\n", + ports[i]); + fini(); + return 1; } + ports_c++; } return ret; } diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_proto_tcp.c linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_proto_tcp.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_proto_tcp.c Tue Aug 7 17:30:50 2001 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_proto_tcp.c Fri Nov 29 00:53:15 2002 @@ -4,7 +4,6 @@ #include #include #include - #include #include #include diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_proto_unknown.c linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_proto_unknown.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_proto_unknown.c Fri Mar 17 19:56:20 2000 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_proto_unknown.c Fri Nov 29 00:53:15 2002 @@ -1,5 +1,5 @@ /* The "unknown" protocol. This is what is used for protocols we - * don't understand. It's returned by find_proto(). + * don't understand. It's returned by ip_ct_find_proto(). */ #include diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_rule.c linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_rule.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_rule.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_rule.c Fri Nov 29 00:53:15 2002 @@ -106,8 +106,6 @@ = { { NULL, NULL }, "nat", &nat_initial_table.repl, NAT_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE }; -LIST_HEAD(nat_expect_list); - /* Source NAT */ static unsigned int ipt_snat_target(struct sk_buff **pskb, unsigned int hooknum, @@ -254,19 +252,6 @@ return ip_nat_setup_info(conntrack, &mr, hooknum); } -static inline int call_expect(const struct ip_nat_expect *i, - struct sk_buff **pskb, - unsigned int hooknum, - struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, - unsigned int *verdict) -{ - return i->expect(pskb, hooknum, ct, info, master, masterinfo, - verdict); -} - int ip_nat_rule_find(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, @@ -276,41 +261,14 @@ { int ret; - /* Master won't vanish while this ctrack still alive */ - if (ct->master.master) { - struct ip_conntrack *master; - - master = (struct ip_conntrack *)ct->master.master; - if (LIST_FIND(&nat_expect_list, - call_expect, - struct ip_nat_expect *, - pskb, hooknum, ct, info, - master, &master->nat.info, &ret)) - return ret; - } ret = ipt_do_table(pskb, hooknum, in, out, &nat_table, NULL); + if (ret == NF_ACCEPT) { if (!(info->initialized & (1 << HOOK2MANIP(hooknum)))) /* NUL mapping */ ret = alloc_null_binding(ct, info, hooknum); } return ret; -} - -int ip_nat_expect_register(struct ip_nat_expect *expect) -{ - WRITE_LOCK(&ip_nat_lock); - list_prepend(&nat_expect_list, expect); - WRITE_UNLOCK(&ip_nat_lock); - - return 0; -} - -void ip_nat_expect_unregister(struct ip_nat_expect *expect) -{ - WRITE_LOCK(&ip_nat_lock); - LIST_DELETE(&nat_expect_list, expect); - WRITE_UNLOCK(&ip_nat_lock); } static struct ipt_target ipt_snat_reg diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_snmp_basic.c linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_snmp_basic.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_snmp_basic.c Wed Oct 31 00:08:12 2001 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_snmp_basic.c Fri Nov 29 00:53:15 2002 @@ -1243,6 +1243,7 @@ * NAT helper function, packets arrive here from NAT code. */ static unsigned int nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, struct ip_nat_info *info, enum ip_conntrack_info ctinfo, unsigned int hooknum, @@ -1259,9 +1260,9 @@ * on post routing (SNAT). */ if (!((dir == IP_CT_DIR_REPLY && hooknum == NF_IP_PRE_ROUTING && - udph->source == __constant_ntohs(SNMP_PORT)) || + udph->source == ntohs(SNMP_PORT)) || (dir == IP_CT_DIR_ORIGINAL && hooknum == NF_IP_POST_ROUTING && - udph->dest == __constant_ntohs(SNMP_TRAP_PORT)))) { + udph->dest == ntohs(SNMP_TRAP_PORT)))) { spin_unlock_bh(&snmp_lock); return NF_ACCEPT; } @@ -1303,19 +1304,27 @@ return NF_DROP; } -static struct ip_nat_helper snmp = { { NULL, NULL }, +static struct ip_nat_helper snmp = { + { NULL, NULL }, + "snmp", + IP_NAT_HELPER_F_STANDALONE, + THIS_MODULE, { { 0, { __constant_htons(SNMP_PORT) } }, { 0, { 0 }, IPPROTO_UDP } }, { { 0, { 0xFFFF } }, { 0, { 0 }, 0xFFFF } }, - nat_help, "snmp" }; + nat_help, NULL }; -static struct ip_nat_helper snmp_trap = { { NULL, NULL }, +static struct ip_nat_helper snmp_trap = { + { NULL, NULL }, + "snmp_trap", + IP_NAT_HELPER_F_STANDALONE, + THIS_MODULE, { { 0, { __constant_htons(SNMP_TRAP_PORT) } }, { 0, { 0 }, IPPROTO_UDP } }, { { 0, { 0xFFFF } }, { 0, { 0 }, 0xFFFF } }, - nat_help, "snmp_trap" }; + nat_help, NULL }; /***************************************************************************** * diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_standalone.c linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_standalone.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_nat_standalone.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_nat_standalone.c Fri Nov 29 00:53:15 2002 @@ -5,7 +5,12 @@ */ /* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General - Public Licence. */ + * Public Licence. + * + * 23 Apr 2001: Harald Welte + * - new API and handling of conntrack/nat helpers + * - now capable of multiple expectations for one master + * */ #include #include @@ -44,6 +49,15 @@ : ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN" \ : "*ERROR*"))) +static inline int call_expect(struct ip_conntrack *master, + struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + return master->nat.info.helper->expect(pskb, hooknum, ct, info); +} + static unsigned int ip_nat_fn(unsigned int hooknum, struct sk_buff **pskb, @@ -60,7 +74,7 @@ /* We never see fragments: conntrack defrags on pre-routing and local-out, and ip_nat_out protects post-routing. */ IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off - & __constant_htons(IP_MF|IP_OFFSET))); + & htons(IP_MF|IP_OFFSET))); (*pskb)->nfcache |= NFC_UNKNOWN; @@ -110,8 +124,16 @@ int in_hashes = info->initialized; unsigned int ret; - ret = ip_nat_rule_find(pskb, hooknum, in, out, - ct, info); + if (ct->master + && master_ct(ct)->nat.info.helper + && master_ct(ct)->nat.info.helper->expect) { + ret = call_expect(master_ct(ct), pskb, + hooknum, ct, info); + } else { + ret = ip_nat_rule_find(pskb, hooknum, in, out, + ct, info); + } + if (ret != NF_ACCEPT) { WRITE_UNLOCK(&ip_nat_lock); return ret; @@ -163,7 +185,7 @@ I'm starting to have nightmares about fragments. */ - if ((*pskb)->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) { + if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { *pskb = ip_ct_gather_frags(*pskb); if (!*pskb) @@ -334,11 +356,7 @@ EXPORT_SYMBOL(ip_nat_protocol_unregister); EXPORT_SYMBOL(ip_nat_helper_register); EXPORT_SYMBOL(ip_nat_helper_unregister); -EXPORT_SYMBOL(ip_nat_expect_register); -EXPORT_SYMBOL(ip_nat_expect_unregister); EXPORT_SYMBOL(ip_nat_cheat_check); EXPORT_SYMBOL(ip_nat_mangle_tcp_packet); -EXPORT_SYMBOL(ip_nat_seq_adjust); -EXPORT_SYMBOL(ip_nat_delete_sack); EXPORT_SYMBOL(ip_nat_used_tuple); MODULE_LICENSE("GPL"); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_queue.c linux-2.4.20-plain/net/ipv4/netfilter/ip_queue.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_queue.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_queue.c Fri Nov 29 00:53:15 2002 @@ -2,13 +2,14 @@ * This is a module which is used for queueing IPv4 packets and * communicating with userspace via netlink. * - * (C) 2000 James Morris, this code is GPL. + * (C) 2000-2002 James Morris, this code is GPL. * * 2000-03-27: Simplified code (thanks to Andi Kleen for clues). * 2000-05-20: Fixed notifier problems (following Miguel Freitas' report). * 2000-06-19: Fixed so nfmark is copied to metadata (reported by Sebastian * Zander). * 2000-08-01: Added Nick Williams' MAC support. + * 2002-06-25: Code cleanup. * */ #include @@ -18,205 +19,310 @@ #include #include #include +#include +#include #include #include -#include +#include #include #include #include #include -#include -#include - #define IPQ_QMAX_DEFAULT 1024 #define IPQ_PROC_FS_NAME "ip_queue" #define NET_IPQ_QMAX 2088 #define NET_IPQ_QMAX_NAME "ip_queue_maxlen" -typedef struct ipq_rt_info { +struct ipq_rt_info { __u8 tos; __u32 daddr; __u32 saddr; -} ipq_rt_info_t; +}; -typedef struct ipq_queue_element { - struct list_head list; /* Links element into queue */ - int verdict; /* Current verdict */ - struct nf_info *info; /* Extra info from netfilter */ - struct sk_buff *skb; /* Packet inside */ - ipq_rt_info_t rt_info; /* May need post-mangle routing */ -} ipq_queue_element_t; - -typedef int (*ipq_send_cb_t)(ipq_queue_element_t *e); - -typedef struct ipq_peer { - pid_t pid; /* PID of userland peer */ - unsigned char died; /* We think the peer died */ - unsigned char copy_mode; /* Copy packet as well as metadata? */ - size_t copy_range; /* Range past metadata to copy */ - ipq_send_cb_t send; /* Callback for sending data to peer */ -} ipq_peer_t; - -typedef struct ipq_queue { - int len; /* Current queue len */ - int *maxlen; /* Maximum queue len, via sysctl */ - unsigned char flushing; /* If queue is being flushed */ - unsigned char terminate; /* If the queue is being terminated */ - struct list_head list; /* Head of packet queue */ - spinlock_t lock; /* Queue spinlock */ - ipq_peer_t peer; /* Userland peer */ -} ipq_queue_t; +struct ipq_queue_entry { + struct list_head list; + struct nf_info *info; + struct sk_buff *skb; + struct ipq_rt_info rt_info; +}; -/**************************************************************************** - * - * Packet queue - * - ****************************************************************************/ -/* Dequeue a packet if matched by cmp, or the next available if cmp is NULL */ -static ipq_queue_element_t * -ipq_dequeue(ipq_queue_t *q, - int (*cmp)(ipq_queue_element_t *, unsigned long), - unsigned long data) -{ - struct list_head *i; - - spin_lock_bh(&q->lock); - for (i = q->list.prev; i != &q->list; i = i->prev) { - ipq_queue_element_t *e = (ipq_queue_element_t *)i; - - if (!cmp || cmp(e, data)) { - list_del(&e->list); - q->len--; - spin_unlock_bh(&q->lock); - return e; - } +typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long); + +static unsigned char copy_mode = IPQ_COPY_NONE; +static unsigned int queue_maxlen = IPQ_QMAX_DEFAULT; +static rwlock_t queue_lock = RW_LOCK_UNLOCKED; +static int peer_pid; +static unsigned int copy_range; +static unsigned int queue_total; +static struct sock *ipqnl; +static LIST_HEAD(queue_list); +static DECLARE_MUTEX(ipqnl_sem); + +static void +ipq_issue_verdict(struct ipq_queue_entry *entry, int verdict) +{ + nf_reinject(entry->skb, entry->info, verdict); + kfree(entry); +} + +static inline int +__ipq_enqueue_entry(struct ipq_queue_entry *entry) +{ + if (queue_total >= queue_maxlen) { + if (net_ratelimit()) + printk(KERN_WARNING "ip_queue: full at %d entries, " + "dropping packet(s).\n", queue_total); + return -ENOSPC; + } + list_add(&entry->list, &queue_list); + queue_total++; + return 0; +} + +/* + * Find and return a queued entry matched by cmpfn, or return the last + * entry if cmpfn is NULL. + */ +static inline struct ipq_queue_entry * +__ipq_find_entry(ipq_cmpfn cmpfn, unsigned long data) +{ + struct list_head *p; + + list_for_each_prev(p, &queue_list) { + struct ipq_queue_entry *entry = (struct ipq_queue_entry *)p; + + if (!cmpfn || cmpfn(entry, data)) + return entry; } - spin_unlock_bh(&q->lock); return NULL; } -/* Flush all packets */ -static void ipq_flush(ipq_queue_t *q) +static inline void +__ipq_dequeue_entry(struct ipq_queue_entry *entry) { - ipq_queue_element_t *e; - - spin_lock_bh(&q->lock); - q->flushing = 1; - spin_unlock_bh(&q->lock); - while ((e = ipq_dequeue(q, NULL, 0))) { - e->verdict = NF_DROP; - nf_reinject(e->skb, e->info, e->verdict); - kfree(e); - } - spin_lock_bh(&q->lock); - q->flushing = 0; - spin_unlock_bh(&q->lock); -} - -static ipq_queue_t *ipq_create_queue(nf_queue_outfn_t outfn, - ipq_send_cb_t send_cb, - int *errp, int *sysctl_qmax) + list_del(&entry->list); + queue_total--; +} + +static inline struct ipq_queue_entry * +__ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data) { - int status; - ipq_queue_t *q; + struct ipq_queue_entry *entry; - *errp = 0; - q = kmalloc(sizeof(ipq_queue_t), GFP_KERNEL); - if (q == NULL) { - *errp = -ENOMEM; + entry = __ipq_find_entry(cmpfn, data); + if (entry == NULL) return NULL; + + __ipq_dequeue_entry(entry); + return entry; +} + + +static inline void +__ipq_flush(int verdict) +{ + struct ipq_queue_entry *entry; + + while ((entry = __ipq_find_dequeue_entry(NULL, 0))) + ipq_issue_verdict(entry, verdict); +} + +static inline int +__ipq_set_mode(unsigned char mode, unsigned int range) +{ + int status = 0; + + switch(mode) { + case IPQ_COPY_NONE: + case IPQ_COPY_META: + copy_mode = mode; + copy_range = 0; + break; + + case IPQ_COPY_PACKET: + copy_mode = mode; + copy_range = range; + if (copy_range > 0xFFFF) + copy_range = 0xFFFF; + break; + + default: + status = -EINVAL; + } - q->peer.pid = 0; - q->peer.died = 0; - q->peer.copy_mode = IPQ_COPY_NONE; - q->peer.copy_range = 0; - q->peer.send = send_cb; - q->len = 0; - q->maxlen = sysctl_qmax; - q->flushing = 0; - q->terminate = 0; - INIT_LIST_HEAD(&q->list); - spin_lock_init(&q->lock); - status = nf_register_queue_handler(PF_INET, outfn, q); - if (status < 0) { - *errp = -EBUSY; - kfree(q); + return status; +} + +static inline void +__ipq_reset(void) +{ + peer_pid = 0; + __ipq_set_mode(IPQ_COPY_NONE, 0); + __ipq_flush(NF_DROP); +} + +static struct ipq_queue_entry * +ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data) +{ + struct ipq_queue_entry *entry; + + write_lock_bh(&queue_lock); + entry = __ipq_find_dequeue_entry(cmpfn, data); + write_unlock_bh(&queue_lock); + return entry; +} + +static void +ipq_flush(int verdict) +{ + write_lock_bh(&queue_lock); + __ipq_flush(verdict); + write_unlock_bh(&queue_lock); +} + +static struct sk_buff * +ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp) +{ + unsigned char *old_tail; + size_t size = 0; + size_t data_len = 0; + struct sk_buff *skb; + struct ipq_packet_msg *pmsg; + struct nlmsghdr *nlh; + + read_lock_bh(&queue_lock); + + switch (copy_mode) { + case IPQ_COPY_META: + case IPQ_COPY_NONE: + size = NLMSG_SPACE(sizeof(*pmsg)); + data_len = 0; + break; + + case IPQ_COPY_PACKET: + if (copy_range == 0 || copy_range > entry->skb->len) + data_len = entry->skb->len; + else + data_len = copy_range; + + size = NLMSG_SPACE(sizeof(*pmsg) + data_len); + break; + + default: + *errp = -EINVAL; + read_unlock_bh(&queue_lock); return NULL; } - return q; + + read_unlock_bh(&queue_lock); + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) + goto nlmsg_failure; + + old_tail= skb->tail; + nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh)); + pmsg = NLMSG_DATA(nlh); + memset(pmsg, 0, sizeof(*pmsg)); + + pmsg->packet_id = (unsigned long )entry; + pmsg->data_len = data_len; + pmsg->timestamp_sec = entry->skb->stamp.tv_sec; + pmsg->timestamp_usec = entry->skb->stamp.tv_usec; + pmsg->mark = entry->skb->nfmark; + pmsg->hook = entry->info->hook; + pmsg->hw_protocol = entry->skb->protocol; + + if (entry->info->indev) + strcpy(pmsg->indev_name, entry->info->indev->name); + else + pmsg->indev_name[0] = '\0'; + + if (entry->info->outdev) + strcpy(pmsg->outdev_name, entry->info->outdev->name); + else + pmsg->outdev_name[0] = '\0'; + + if (entry->info->indev && entry->skb->dev) { + pmsg->hw_type = entry->skb->dev->type; + if (entry->skb->dev->hard_header_parse) + pmsg->hw_addrlen = + entry->skb->dev->hard_header_parse(entry->skb, + pmsg->hw_addr); + } + + if (data_len) + memcpy(pmsg->payload, entry->skb->data, data_len); + + nlh->nlmsg_len = skb->tail - old_tail; + return skb; + +nlmsg_failure: + if (skb) + kfree_skb(skb); + *errp = -EINVAL; + printk(KERN_ERR "ip_queue: error creating packet message\n"); + return NULL; } -static int ipq_enqueue(ipq_queue_t *q, - struct sk_buff *skb, struct nf_info *info) +static int +ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info, void *data) { - ipq_queue_element_t *e; - int status; - - e = kmalloc(sizeof(*e), GFP_ATOMIC); - if (e == NULL) { - printk(KERN_ERR "ip_queue: OOM in enqueue\n"); + int status = -EINVAL; + struct sk_buff *nskb; + struct ipq_queue_entry *entry; + + if (copy_mode == IPQ_COPY_NONE) + return -EAGAIN; + + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry == NULL) { + printk(KERN_ERR "ip_queue: OOM in ipq_enqueue_packet()\n"); return -ENOMEM; } - e->verdict = NF_DROP; - e->info = info; - e->skb = skb; + entry->info = info; + entry->skb = skb; - if (e->info->hook == NF_IP_LOCAL_OUT) { + if (entry->info->hook == NF_IP_LOCAL_OUT) { struct iphdr *iph = skb->nh.iph; - e->rt_info.tos = iph->tos; - e->rt_info.daddr = iph->daddr; - e->rt_info.saddr = iph->saddr; - } - - spin_lock_bh(&q->lock); - if (q->len >= *q->maxlen) { - spin_unlock_bh(&q->lock); - if (net_ratelimit()) - printk(KERN_WARNING "ip_queue: full at %d entries, " - "dropping packet(s).\n", q->len); - goto free_drop; - } - if (q->flushing || q->peer.copy_mode == IPQ_COPY_NONE - || q->peer.pid == 0 || q->peer.died || q->terminate) { - spin_unlock_bh(&q->lock); - goto free_drop; - } - status = q->peer.send(e); - if (status > 0) { - list_add(&e->list, &q->list); - q->len++; - spin_unlock_bh(&q->lock); - return status; - } - spin_unlock_bh(&q->lock); - if (status == -ECONNREFUSED) { - printk(KERN_INFO "ip_queue: peer %d died, " - "resetting state and flushing queue\n", q->peer.pid); - q->peer.died = 1; - q->peer.pid = 0; - q->peer.copy_mode = IPQ_COPY_NONE; - q->peer.copy_range = 0; - ipq_flush(q); - } -free_drop: - kfree(e); - return -EBUSY; -} + entry->rt_info.tos = iph->tos; + entry->rt_info.daddr = iph->daddr; + entry->rt_info.saddr = iph->saddr; + } -static void ipq_destroy_queue(ipq_queue_t *q) -{ - nf_unregister_queue_handler(PF_INET); - spin_lock_bh(&q->lock); - q->terminate = 1; - spin_unlock_bh(&q->lock); - ipq_flush(q); - kfree(q); + nskb = ipq_build_packet_message(entry, &status); + if (nskb == NULL) + goto err_out_free; + + write_lock_bh(&queue_lock); + + if (!peer_pid) + goto err_out_unlock; + + status = netlink_unicast(ipqnl, nskb, peer_pid, MSG_DONTWAIT); + if (status < 0) + goto err_out_unlock; + + status = __ipq_enqueue_entry(entry); + if (status < 0) + goto err_out_unlock; + + write_unlock_bh(&queue_lock); + return status; + +err_out_unlock: + write_unlock_bh(&queue_lock); + +err_out_free: + kfree(entry); + return status; } -static int ipq_mangle_ipv4(ipq_verdict_msg_t *v, ipq_queue_element_t *e) +static int +ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct ipq_queue_entry *e) { int diff; struct iphdr *user_iph = (struct iphdr *)v->payload; @@ -266,296 +372,216 @@ return 0; } -static inline int id_cmp(ipq_queue_element_t *e, unsigned long id) +static inline int +id_cmp(struct ipq_queue_entry *e, unsigned long id) { return (id == (unsigned long )e); } -static int ipq_set_verdict(ipq_queue_t *q, - ipq_verdict_msg_t *v, unsigned int len) +static int +ipq_set_verdict(struct ipq_verdict_msg *vmsg, unsigned int len) { - ipq_queue_element_t *e; + struct ipq_queue_entry *entry; - if (v->value > NF_MAX_VERDICT) + if (vmsg->value > NF_MAX_VERDICT) return -EINVAL; - e = ipq_dequeue(q, id_cmp, v->id); - if (e == NULL) + + entry = ipq_find_dequeue_entry(id_cmp, vmsg->id); + if (entry == NULL) return -ENOENT; else { - e->verdict = v->value; - if (v->data_len && v->data_len == len) - if (ipq_mangle_ipv4(v, e) < 0) - e->verdict = NF_DROP; - nf_reinject(e->skb, e->info, e->verdict); - kfree(e); + int verdict = vmsg->value; + + if (vmsg->data_len && vmsg->data_len == len) + if (ipq_mangle_ipv4(vmsg, entry) < 0) + verdict = NF_DROP; + + ipq_issue_verdict(entry, verdict); return 0; } } -static int ipq_receive_peer(ipq_queue_t *q, ipq_peer_msg_t *m, - unsigned char type, unsigned int len) +static int +ipq_set_mode(unsigned char mode, unsigned int range) { + int status; + + write_lock_bh(&queue_lock); + status = __ipq_set_mode(mode, range); + write_unlock_bh(&queue_lock); + return status; +} +static int +ipq_receive_peer(struct ipq_peer_msg *pmsg, + unsigned char type, unsigned int len) +{ int status = 0; - int busy; - - spin_lock_bh(&q->lock); - busy = (q->terminate || q->flushing); - spin_unlock_bh(&q->lock); - if (busy) - return -EBUSY; - if (len < sizeof(ipq_peer_msg_t)) + + if (len < sizeof(*pmsg)) return -EINVAL; + switch (type) { - case IPQM_MODE: - switch (m->msg.mode.value) { - case IPQ_COPY_META: - q->peer.copy_mode = IPQ_COPY_META; - q->peer.copy_range = 0; - break; - case IPQ_COPY_PACKET: - q->peer.copy_mode = IPQ_COPY_PACKET; - q->peer.copy_range = m->msg.mode.range; - if (q->peer.copy_range > 0xFFFF) - q->peer.copy_range = 0xFFFF; - break; - default: - status = -EINVAL; - } - break; - case IPQM_VERDICT: - if (m->msg.verdict.value > NF_MAX_VERDICT) - status = -EINVAL; - else - status = ipq_set_verdict(q, - &m->msg.verdict, - len - sizeof(*m)); + case IPQM_MODE: + status = ipq_set_mode(pmsg->msg.mode.value, + pmsg->msg.mode.range); + break; + + case IPQM_VERDICT: + if (pmsg->msg.verdict.value > NF_MAX_VERDICT) + status = -EINVAL; + else + status = ipq_set_verdict(&pmsg->msg.verdict, + len - sizeof(*pmsg)); break; - default: - status = -EINVAL; + default: + status = -EINVAL; } return status; } -static inline int dev_cmp(ipq_queue_element_t *e, unsigned long ifindex) +static int +dev_cmp(struct ipq_queue_entry *entry, unsigned long ifindex) { - if (e->info->indev) - if (e->info->indev->ifindex == ifindex) + if (entry->info->indev) + if (entry->info->indev->ifindex == ifindex) return 1; - if (e->info->outdev) - if (e->info->outdev->ifindex == ifindex) + + if (entry->info->outdev) + if (entry->info->outdev->ifindex == ifindex) return 1; + return 0; } -/* Drop any queued packets associated with device ifindex */ -static void ipq_dev_drop(ipq_queue_t *q, int ifindex) +static void +ipq_dev_drop(int ifindex) { - ipq_queue_element_t *e; + struct ipq_queue_entry *entry; - while ((e = ipq_dequeue(q, dev_cmp, ifindex))) { - e->verdict = NF_DROP; - nf_reinject(e->skb, e->info, e->verdict); - kfree(e); - } -} - -/**************************************************************************** - * - * Netfilter interface - * - ****************************************************************************/ - -/* - * Packets arrive here from netfilter for queuing to userspace. - * All of them must be fed back via nf_reinject() or Alexey will kill Rusty. - */ -static int netfilter_receive(struct sk_buff *skb, - struct nf_info *info, void *data) -{ - return ipq_enqueue((ipq_queue_t *)data, skb, info); -} - -/**************************************************************************** - * - * Netlink interface. - * - ****************************************************************************/ - -static struct sock *nfnl = NULL; -ipq_queue_t *nlq = NULL; - -static struct sk_buff *netlink_build_message(ipq_queue_element_t *e, int *errp) -{ - unsigned char *old_tail; - size_t size = 0; - size_t data_len = 0; - struct sk_buff *skb; - ipq_packet_msg_t *pm; - struct nlmsghdr *nlh; - - switch (nlq->peer.copy_mode) { - size_t copy_range; - - case IPQ_COPY_META: - size = NLMSG_SPACE(sizeof(*pm)); - data_len = 0; - break; - case IPQ_COPY_PACKET: - copy_range = nlq->peer.copy_range; - if (copy_range == 0 || copy_range > e->skb->len) - data_len = e->skb->len; - else - data_len = copy_range; - size = NLMSG_SPACE(sizeof(*pm) + data_len); - - break; - case IPQ_COPY_NONE: - default: - *errp = -EINVAL; - return NULL; - } - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) - goto nlmsg_failure; - old_tail = skb->tail; - nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh)); - pm = NLMSG_DATA(nlh); - memset(pm, 0, sizeof(*pm)); - pm->packet_id = (unsigned long )e; - pm->data_len = data_len; - pm->timestamp_sec = e->skb->stamp.tv_sec; - pm->timestamp_usec = e->skb->stamp.tv_usec; - pm->mark = e->skb->nfmark; - pm->hook = e->info->hook; - if (e->info->indev) strcpy(pm->indev_name, e->info->indev->name); - else pm->indev_name[0] = '\0'; - if (e->info->outdev) strcpy(pm->outdev_name, e->info->outdev->name); - else pm->outdev_name[0] = '\0'; - pm->hw_protocol = e->skb->protocol; - if (e->info->indev && e->skb->dev) { - pm->hw_type = e->skb->dev->type; - if (e->skb->dev->hard_header_parse) - pm->hw_addrlen = - e->skb->dev->hard_header_parse(e->skb, - pm->hw_addr); - } - if (data_len) - memcpy(pm->payload, e->skb->data, data_len); - nlh->nlmsg_len = skb->tail - old_tail; - NETLINK_CB(skb).dst_groups = 0; - return skb; -nlmsg_failure: - if (skb) - kfree_skb(skb); - *errp = 0; - printk(KERN_ERR "ip_queue: error creating netlink message\n"); - return NULL; -} - -static int netlink_send_peer(ipq_queue_element_t *e) -{ - int status = 0; - struct sk_buff *skb; - - skb = netlink_build_message(e, &status); - if (skb == NULL) - return status; - return netlink_unicast(nfnl, skb, nlq->peer.pid, MSG_DONTWAIT); + while ((entry = ipq_find_dequeue_entry(dev_cmp, ifindex)) != NULL) + ipq_issue_verdict(entry, NF_DROP); } #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) -static __inline__ void netlink_receive_user_skb(struct sk_buff *skb) +static inline void +ipq_rcv_skb(struct sk_buff *skb) { - int status, type; + int status, type, pid, flags, nlmsglen, skblen; struct nlmsghdr *nlh; - if (skb->len < sizeof(struct nlmsghdr)) + skblen = skb->len; + if (skblen < sizeof(*nlh)) return; nlh = (struct nlmsghdr *)skb->data; - if (nlh->nlmsg_len < sizeof(struct nlmsghdr) - || skb->len < nlh->nlmsg_len) - return; - - if(nlh->nlmsg_pid <= 0 - || !(nlh->nlmsg_flags & NLM_F_REQUEST) - || nlh->nlmsg_flags & NLM_F_MULTI) + nlmsglen = nlh->nlmsg_len; + if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen) + return; + + pid = nlh->nlmsg_pid; + flags = nlh->nlmsg_flags; + + if(pid <= 0 || !(flags & NLM_F_REQUEST) || flags & NLM_F_MULTI) RCV_SKB_FAIL(-EINVAL); - if (nlh->nlmsg_flags & MSG_TRUNC) + + if (flags & MSG_TRUNC) RCV_SKB_FAIL(-ECOMM); + type = nlh->nlmsg_type; if (type < NLMSG_NOOP || type >= IPQM_MAX) RCV_SKB_FAIL(-EINVAL); + if (type <= IPQM_BASE) return; + if(!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) RCV_SKB_FAIL(-EPERM); - if (nlq->peer.pid && !nlq->peer.died - && (nlq->peer.pid != nlh->nlmsg_pid)) { - printk(KERN_WARNING "ip_queue: peer pid changed from %d to " - "%d, flushing queue\n", nlq->peer.pid, nlh->nlmsg_pid); - ipq_flush(nlq); - } - nlq->peer.pid = nlh->nlmsg_pid; - nlq->peer.died = 0; - status = ipq_receive_peer(nlq, NLMSG_DATA(nlh), - type, skb->len - NLMSG_LENGTH(0)); + + write_lock_bh(&queue_lock); + + if (peer_pid) { + if (peer_pid != pid) { + write_unlock_bh(&queue_lock); + RCV_SKB_FAIL(-EBUSY); + } + } + else + peer_pid = pid; + + write_unlock_bh(&queue_lock); + + status = ipq_receive_peer(NLMSG_DATA(nlh), type, + skblen - NLMSG_LENGTH(0)); if (status < 0) RCV_SKB_FAIL(status); - if (nlh->nlmsg_flags & NLM_F_ACK) + + if (flags & NLM_F_ACK) netlink_ack(skb, nlh, 0); return; } -/* Note: we are only dealing with single part messages at the moment. */ -static void netlink_receive_user_sk(struct sock *sk, int len) +static void +ipq_rcv_sk(struct sock *sk, int len) { do { struct sk_buff *skb; - if (rtnl_shlock_nowait()) + if (down_trylock(&ipqnl_sem)) return; + while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { - netlink_receive_user_skb(skb); + ipq_rcv_skb(skb); kfree_skb(skb); } - up(&rtnl_sem); - } while (nfnl && nfnl->receive_queue.qlen); -} + + up(&ipqnl_sem); -/**************************************************************************** - * - * System events - * - ****************************************************************************/ + } while (ipqnl && ipqnl->receive_queue.qlen); +} -static int receive_event(struct notifier_block *this, - unsigned long event, void *ptr) +static int +ipq_rcv_dev_event(struct notifier_block *this, + unsigned long event, void *ptr) { struct net_device *dev = ptr; /* Drop any packets associated with the downed device */ if (event == NETDEV_DOWN) - ipq_dev_drop(nlq, dev->ifindex); + ipq_dev_drop(dev->ifindex); return NOTIFY_DONE; } -struct notifier_block ipq_dev_notifier = { - receive_event, +static struct notifier_block ipq_dev_notifier = { + ipq_rcv_dev_event, NULL, 0 }; -/**************************************************************************** - * - * Sysctl - queue tuning. - * - ****************************************************************************/ +static int +ipq_rcv_nl_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct netlink_notify *n = ptr; + + if (event == NETLINK_URELEASE && + n->protocol == NETLINK_FIREWALL && n->pid) { + write_lock_bh(&queue_lock); + if (n->pid == peer_pid) + __ipq_reset(); + write_unlock_bh(&queue_lock); + } + return NOTIFY_DONE; +} -static int sysctl_maxlen = IPQ_QMAX_DEFAULT; +static struct notifier_block ipq_nl_notifier = { + ipq_rcv_nl_event, + NULL, + 0 +}; +static int sysctl_maxlen = IPQ_QMAX_DEFAULT; static struct ctl_table_header *ipq_sysctl_header; static ctl_table ipq_table[] = { @@ -574,35 +600,27 @@ { 0 } }; -/**************************************************************************** - * - * Procfs - debugging info. - * - ****************************************************************************/ - -static int ipq_get_info(char *buffer, char **start, off_t offset, int length) +static int +ipq_get_info(char *buffer, char **start, off_t offset, int length) { int len; - spin_lock_bh(&nlq->lock); + read_lock_bh(&queue_lock); + len = sprintf(buffer, - "Peer pid : %d\n" - "Peer died : %d\n" - "Peer copy mode : %d\n" - "Peer copy range : %Zu\n" - "Queue length : %d\n" - "Queue max. length : %d\n" - "Queue flushing : %d\n" - "Queue terminate : %d\n", - nlq->peer.pid, - nlq->peer.died, - nlq->peer.copy_mode, - nlq->peer.copy_range, - nlq->len, - *nlq->maxlen, - nlq->flushing, - nlq->terminate); - spin_unlock_bh(&nlq->lock); + "Peer PID : %d\n" + "Copy mode : %hu\n" + "Copy range : %u\n" + "Queue length : %u\n" + "Queue max. length : %u\n", + peer_pid, + copy_mode, + copy_range, + queue_total, + queue_maxlen); + + read_unlock_bh(&queue_lock); + *start = buffer + offset; len -= offset; if (len > length) @@ -612,53 +630,74 @@ return len; } -/**************************************************************************** - * - * Module stuff. - * - ****************************************************************************/ - -static int __init init(void) +static int +init_or_cleanup(int init) { - int status = 0; + int status = -ENOMEM; struct proc_dir_entry *proc; - nfnl = netlink_kernel_create(NETLINK_FIREWALL, netlink_receive_user_sk); - if (nfnl == NULL) { - printk(KERN_ERR "ip_queue: initialisation failed: unable to " - "create kernel netlink socket\n"); - return -ENOMEM; - } - nlq = ipq_create_queue(netfilter_receive, - netlink_send_peer, &status, &sysctl_maxlen); - if (nlq == NULL) { - printk(KERN_ERR "ip_queue: initialisation failed: unable to " - "create queue\n"); - sock_release(nfnl->socket); - return status; + if (!init) + goto cleanup; + + netlink_register_notifier(&ipq_nl_notifier); + ipqnl = netlink_kernel_create(NETLINK_FIREWALL, ipq_rcv_sk); + if (ipqnl == NULL) { + printk(KERN_ERR "ip_queue: failed to create netlink socket\n"); + goto cleanup_netlink_notifier; } + proc = proc_net_create(IPQ_PROC_FS_NAME, 0, ipq_get_info); - if (proc) proc->owner = THIS_MODULE; + if (proc) + proc->owner = THIS_MODULE; else { - ipq_destroy_queue(nlq); - sock_release(nfnl->socket); - return -ENOMEM; + printk(KERN_ERR "ip_queue: failed to create proc entry\n"); + goto cleanup_ipqnl; } + register_netdevice_notifier(&ipq_dev_notifier); ipq_sysctl_header = register_sysctl_table(ipq_root_table, 0); + + status = nf_register_queue_handler(PF_INET, ipq_enqueue_packet, NULL); + if (status < 0) { + printk(KERN_ERR "ip_queue: failed to register queue handler\n"); + goto cleanup_sysctl; + } + return status; + +cleanup: + nf_unregister_queue_handler(PF_INET); + br_write_lock_bh(BR_NETPROTO_LOCK); + br_write_unlock_bh(BR_NETPROTO_LOCK); + ipq_flush(NF_DROP); + +cleanup_sysctl: + unregister_sysctl_table(ipq_sysctl_header); + unregister_netdevice_notifier(&ipq_dev_notifier); + proc_net_remove(IPQ_PROC_FS_NAME); + +cleanup_ipqnl: + sock_release(ipqnl->socket); + down(&ipqnl_sem); + up(&ipqnl_sem); + +cleanup_netlink_notifier: + netlink_unregister_notifier(&ipq_nl_notifier); return status; } +static int __init init(void) +{ + + return init_or_cleanup(1); +} + static void __exit fini(void) { - unregister_sysctl_table(ipq_sysctl_header); - proc_net_remove(IPQ_PROC_FS_NAME); - unregister_netdevice_notifier(&ipq_dev_notifier); - ipq_destroy_queue(nlq); - sock_release(nfnl->socket); + init_or_cleanup(0); } MODULE_DESCRIPTION("IPv4 packet queue handler"); +MODULE_AUTHOR("James Morris "); MODULE_LICENSE("GPL"); module_init(init); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ip_tables.c linux-2.4.20-plain/net/ipv4/netfilter/ip_tables.c --- linux-2.4.19-plain/net/ipv4/netfilter/ip_tables.c Mon Feb 25 20:38:14 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ip_tables.c Fri Nov 29 00:53:15 2002 @@ -9,6 +9,7 @@ * a table */ #include +#include #include #include #include @@ -97,7 +98,7 @@ unsigned int underflow[NF_IP_NUMHOOKS]; /* ipt_entry tables: one per CPU */ - char entries[0] __attribute__((aligned(SMP_CACHE_BYTES))); + char entries[0] ____cacheline_aligned; }; static LIST_HEAD(ipt_target); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipchains_core.c linux-2.4.20-plain/net/ipv4/netfilter/ipchains_core.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipchains_core.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipchains_core.c Fri Nov 29 00:53:15 2002 @@ -1252,7 +1252,7 @@ return NULL; } - fwkern = kmalloc(SIZEOF_STRUCT_IP_FW_KERNEL, GFP_KERNEL); + fwkern = kmalloc(SIZEOF_STRUCT_IP_FW_KERNEL, GFP_ATOMIC); if (!fwkern) { duprintf("convert_ipfw: kmalloc failed!\n"); *errno = ENOMEM; @@ -1779,4 +1779,4 @@ #endif return ret; } -MODULE_LICENSE("BSD without advertisement clause"); +MODULE_LICENSE("Dual BSD/GPL"); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_DSCP.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_DSCP.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_DSCP.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_DSCP.c Fri Nov 29 00:53:15 2002 @@ -0,0 +1,108 @@ +/* iptables module for setting the IPv4 DSCP field, Version 1.8 + * + * (C) 2002 by Harald Welte + * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh + * This software is distributed under GNU GPL v2, 1991 + * + * See RFC2474 for a description of the DSCP field within the IP Header. + * + * ipt_DSCP.c,v 1.8 2002/08/06 18:41:57 laforge Exp +*/ + +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("IP tables DSCP modification module"); +MODULE_LICENSE("GPL"); + +static unsigned int +target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + struct iphdr *iph = (*pskb)->nh.iph; + const struct ipt_DSCP_info *dinfo = targinfo; + u_int8_t sh_dscp = ((dinfo->dscp << IPT_DSCP_SHIFT) & IPT_DSCP_MASK); + + + if ((iph->tos & IPT_DSCP_MASK) != sh_dscp) { + u_int16_t diffs[2]; + + /* raw socket (tcpdump) may have clone of incoming + * skb: don't disturb it --RR */ + if (skb_cloned(*pskb) && !(*pskb)->sk) { + struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); + if (!nskb) + return NF_DROP; + kfree_skb(*pskb); + *pskb = nskb; + iph = (*pskb)->nh.iph; + } + + diffs[0] = htons(iph->tos) ^ 0xFFFF; + iph->tos = (iph->tos & ~IPT_DSCP_MASK) | sh_dscp; + diffs[1] = htons(iph->tos); + iph->check = csum_fold(csum_partial((char *)diffs, + sizeof(diffs), + iph->check^0xFFFF)); + (*pskb)->nfcache |= NFC_ALTERED; + } + return IPT_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + const u_int8_t dscp = ((struct ipt_DSCP_info *)targinfo)->dscp; + + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_DSCP_info))) { + printk(KERN_WARNING "DSCP: targinfosize %u != %Zu\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_DSCP_info))); + return 0; + } + + if (strcmp(tablename, "mangle") != 0) { + printk(KERN_WARNING "DSCP: can only be called from \"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + + if ((dscp > IPT_DSCP_MAX)) { + printk(KERN_WARNING "DSCP: dscp %x out of range\n", dscp); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_dscp_reg += { { NULL, NULL }, "DSCP", target, checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_target(&ipt_dscp_reg)) + return -EINVAL; + + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_dscp_reg); +} + +module_init(init); +module_exit(fini); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_ECN.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_ECN.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_ECN.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_ECN.c Fri Nov 29 00:53:15 2002 @@ -0,0 +1,183 @@ +/* iptables module for the IPv4 and TCP ECN bits, Version 1.2 + * + * (C) 2002 by Harald Welte + * + * This software is distributed under GNU GPL v2, 1991 + * + * ipt_ECN.c,v 1.4 2002/08/05 19:36:51 laforge Exp +*/ + +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +/* set ECT codepoint from IP header. + * return 0 in case there was no ECT codepoint + * return 1 in case ECT codepoint has been overwritten + * return < 0 in case there was error */ +static int inline +set_ect_ip(struct sk_buff **pskb, struct iphdr *iph, + const struct ipt_ECN_info *einfo) +{ + if ((iph->tos & IPT_ECN_IP_MASK) + != (einfo->ip_ect & IPT_ECN_IP_MASK)) { + u_int16_t diffs[2]; + + /* raw socket (tcpdump) may have clone of incoming + * skb: don't disturb it --RR */ + if (skb_cloned(*pskb) && !(*pskb)->sk) { + struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); + if (!nskb) + return NF_DROP; + kfree_skb(*pskb); + *pskb = nskb; + iph = (*pskb)->nh.iph; + } + + diffs[0] = htons(iph->tos) ^ 0xFFFF; + iph->tos = iph->tos & ~IPT_ECN_IP_MASK; + iph->tos = iph->tos | (einfo->ip_ect & IPT_ECN_IP_MASK); + diffs[1] = htons(iph->tos); + iph->check = csum_fold(csum_partial((char *)diffs, + sizeof(diffs), + iph->check^0xFFFF)); + (*pskb)->nfcache |= NFC_ALTERED; + + return 1; + } + return 0; +} + +static int inline +set_ect_tcp(struct sk_buff **pskb, struct iphdr *iph, + const struct ipt_ECN_info *einfo) +{ + + struct tcphdr *tcph = (void *) iph + iph->ihl * 4; + u_int16_t *tcpflags = (u_int16_t *)tcph + 6; + u_int16_t diffs[2]; + + /* raw socket (tcpdump) may have clone of incoming + * skb: don't disturb it --RR */ + if (skb_cloned(*pskb) && !(*pskb)->sk) { + struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); + if (!nskb) + return NF_DROP; + kfree_skb(*pskb); + *pskb = nskb; + iph = (*pskb)->nh.iph; + } + + diffs[0] = *tcpflags; + + if (einfo->operation & IPT_ECN_OP_SET_ECE + && tcph->ece != einfo->proto.tcp.ece) { + tcph->ece = einfo->proto.tcp.ece; + } + + if (einfo->operation & IPT_ECN_OP_SET_CWR + && tcph->cwr != einfo->proto.tcp.cwr) { + tcph->cwr = einfo->proto.tcp.cwr; + } + + if (diffs[0] != *tcpflags) { + diffs[0] = htons(diffs[0]) ^ 0xFFFF; + diffs[1] = htons(*tcpflags); + tcph->check = csum_fold(csum_partial((char *)diffs, + sizeof(diffs), + tcph->check^0xFFFF)); + (*pskb)->nfcache |= NFC_ALTERED; + + return 1; + } + + return 0; +} + +static unsigned int +target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + struct iphdr *iph = (*pskb)->nh.iph; + const struct ipt_ECN_info *einfo = targinfo; + + if (einfo->operation & IPT_ECN_OP_SET_IP) + set_ect_ip(pskb, iph, einfo); + + if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR) + && iph->protocol == IPPROTO_TCP) + set_ect_tcp(pskb, iph, einfo); + + return IPT_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + const struct ipt_ECN_info *einfo = (struct ipt_ECN_info *)targinfo; + + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_ECN_info))) { + printk(KERN_WARNING "ECN: targinfosize %u != %Zu\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_ECN_info))); + return 0; + } + + if (strcmp(tablename, "mangle") != 0) { + printk(KERN_WARNING "ECN: can only be called from \"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + + if (einfo->operation & IPT_ECN_OP_MASK) { + printk(KERN_WARNING "ECN: unsupported ECN operation %x\n", + einfo->operation); + return 0; + } + if (einfo->ip_ect & ~IPT_ECN_IP_MASK) { + printk(KERN_WARNING "ECN: new ECT codepoint %x out of mask\n", + einfo->ip_ect); + return 0; + } + + if ((einfo->operation & (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)) + && e->ip.proto != IPPROTO_TCP) { + printk(KERN_WARNING "ECN: cannot use TCP operations on a " + "non-tcp rule\n"); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_ecn_reg += { { NULL, NULL }, "ECN", target, checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_target(&ipt_ecn_reg)) + return -EINVAL; + + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_ecn_reg); +} + +module_init(init); +module_exit(fini); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_REJECT.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_REJECT.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_REJECT.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_REJECT.c Fri Nov 29 00:53:15 2002 @@ -75,6 +75,7 @@ #ifdef CONFIG_NETFILTER_DEBUG nskb->nf_debug = 0; #endif + nskb->nfmark = 0; tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_ULOG.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_ULOG.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_ULOG.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_ULOG.c Fri Nov 29 00:53:15 2002 @@ -10,6 +10,8 @@ * nlgroup now global (sysctl) * 2001/04/19 ulog-queue reworked, now fixed buffer size specified at * module loadtime -HW + * 2002/07/07 remove broken nflog_rcv() function -HW + * 2002/08/29 fix shifted/unshifted nlgroup bug -HW * * Released under the terms of the GPL * @@ -29,7 +31,7 @@ * Specify, after how many clock ticks (intel: 100 per second) the queue * should be flushed even if it is not full yet. * - * ipt_ULOG.c,v 1.18 2002/04/16 07:33:00 laforge Exp + * ipt_ULOG.c,v 1.21 2002/08/29 10:54:34 laforge Exp */ #include @@ -48,8 +50,11 @@ #include #include #include +#include MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("IP tables userspace logging module"); #define ULOG_NL_EVENT 111 /* Harald's favorite number */ #define ULOG_MAXNLGROUPS 32 /* numer of nlgroups */ @@ -63,10 +68,6 @@ #define PRINTR(format, args...) do { if (net_ratelimit()) printk(format, ## args); } while (0) -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("IP tables userspace logging module"); - - static unsigned int nlbufsiz = 4096; MODULE_PARM(nlbufsiz, "i"); MODULE_PARM_DESC(nlbufsiz, "netlink buffer size"); @@ -91,9 +92,9 @@ DECLARE_LOCK(ulog_lock); /* spinlock */ /* send one ulog_buff_t to userspace */ -static void ulog_send(unsigned int nlgroup) +static void ulog_send(unsigned int nlgroupnum) { - ulog_buff_t *ub = &ulog_buffers[nlgroup]; + ulog_buff_t *ub = &ulog_buffers[nlgroupnum]; if (timer_pending(&ub->timer)) { DEBUGP("ipt_ULOG: ulog_send: timer was pending, deleting\n"); @@ -104,10 +105,10 @@ if (ub->qlen > 1) ub->lastnlh->nlmsg_type = NLMSG_DONE; - NETLINK_CB(ub->skb).dst_groups = nlgroup; + NETLINK_CB(ub->skb).dst_groups = (1 << nlgroupnum); DEBUGP("ipt_ULOG: throwing %d packets to netlink mask %u\n", ub->qlen, nlgroup); - netlink_broadcast(nflognl, ub->skb, 0, nlgroup, GFP_ATOMIC); + netlink_broadcast(nflognl, ub->skb, 0, (1 << nlgroupnum), GFP_ATOMIC); ub->qlen = 0; ub->skb = NULL; @@ -128,11 +129,6 @@ UNLOCK_BH(&ulog_lock); } -static void nflog_rcv(struct sock *sk, int len) -{ - printk("ipt_ULOG:nflog_rcv() did receive netlink message ?!?\n"); -} - struct sk_buff *ulog_alloc_skb(unsigned int size) { struct sk_buff *skb; @@ -169,6 +165,11 @@ 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. + * ffs() returns [1..32], we need [0..31] */ + unsigned int groupnum = ffs(loginfo->nl_group) - 1; + /* calculate the size of the skb needed */ if ((loginfo->copy_range == 0) || (loginfo->copy_range > (*pskb)->len)) { @@ -179,7 +180,7 @@ size = NLMSG_SPACE(sizeof(*pm) + copy_len); - ub = &ulog_buffers[loginfo->nl_group]; + ub = &ulog_buffers[groupnum]; LOCK_BH(&ulog_lock); @@ -191,7 +192,7 @@ /* either the queue len is too high or we don't have * enough room in nlskb left. send it to userspace. */ - ulog_send(loginfo->nl_group); + ulog_send(groupnum); if (!(ub->skb = ulog_alloc_skb(size))) goto alloc_failure; @@ -325,7 +326,7 @@ ulog_buffers[i].timer.data = i; } - nflognl = netlink_kernel_create(NETLINK_NFLOG, nflog_rcv); + nflognl = netlink_kernel_create(NETLINK_NFLOG, NULL); if (!nflognl) return -ENOMEM; diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_ah.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_ah.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_ah.c Mon Feb 25 20:38:14 2002 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_ah.c Fri Nov 29 00:53:15 2002 @@ -91,12 +91,12 @@ static struct ipt_match ah_match = { { NULL, NULL }, "ah", &match, &checkentry, NULL, THIS_MODULE }; -int __init init(void) +static int __init init(void) { return ipt_register_match(&ah_match); } -void __exit cleanup(void) +static void __exit cleanup(void) { ipt_unregister_match(&ah_match); } diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_conntrack.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_conntrack.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_conntrack.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_conntrack.c Fri Nov 29 00:53:15 2002 @@ -0,0 +1,123 @@ +/* Kernel module to match connection tracking information. + * Superset of Rusty's minimalistic state match. + * GPL (C) 2001 Marc Boucher (marc@mbsi.ca). + */ +#include +#include +#include +#include +#include + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_conntrack_info *sinfo = matchinfo; + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + unsigned int statebit; + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + +#define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg)) + + statebit = ct ? IPT_CONNTRACK_STATE_INVALID : IPT_CONNTRACK_STATE_BIT(ctinfo); + if(sinfo->flags & IPT_CONNTRACK_STATE) { + if (ct) { + if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip != + ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip) + statebit |= IPT_CONNTRACK_STATE_SNAT; + + if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip != + ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip) + statebit |= IPT_CONNTRACK_STATE_DNAT; + } + + if (FWINV((statebit & sinfo->statemask) == 0, IPT_CONNTRACK_STATE)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_PROTO) { + if (!ct || FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, IPT_CONNTRACK_PROTO)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_ORIGSRC) { + if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip&sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, IPT_CONNTRACK_ORIGSRC)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_ORIGDST) { + if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip&sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, IPT_CONNTRACK_ORIGDST)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_REPLSRC) { + if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip&sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].src.ip, IPT_CONNTRACK_REPLSRC)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_REPLDST) { + if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip&sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, IPT_CONNTRACK_REPLDST)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_STATUS) { + if (!ct || FWINV((ct->status & sinfo->statusmask) == 0, IPT_CONNTRACK_STATUS)) + return 0; + } + + if(sinfo->flags & IPT_CONNTRACK_EXPIRES) { + unsigned long expires; + + if(!ct) + return 0; + + expires = timer_pending(&ct->timeout) ? (ct->timeout.expires - jiffies)/HZ : 0; + + if (FWINV(!(expires >= sinfo->expires_min && expires <= sinfo->expires_max), IPT_CONNTRACK_EXPIRES)) + return 0; + } + + return 1; +} + +static int check(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_conntrack_info))) + return 0; + + return 1; +} + +static struct ipt_match conntrack_match += { { NULL, NULL }, "conntrack", &match, &check, NULL, THIS_MODULE }; + +static int __init init(void) +{ + /* NULL if ip_conntrack not a module */ + if (ip_conntrack_module) + __MOD_INC_USE_COUNT(ip_conntrack_module); + return ipt_register_match(&conntrack_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&conntrack_match); + if (ip_conntrack_module) + __MOD_DEC_USE_COUNT(ip_conntrack_module); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_dscp.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_dscp.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_dscp.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_dscp.c Fri Nov 29 00:53:15 2002 @@ -0,0 +1,58 @@ +/* IP tables module for matching the value of the IPv4 DSCP field + * + * ipt_dscp.c,v 1.3 2002/08/05 19:00:21 laforge Exp + * + * (C) 2002 by Harald Welte + * + * This software is distributed under the terms GNU GPL + */ + +#include +#include + +#include +#include + +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("IP tables DSCP matching module"); +MODULE_LICENSE("GPL"); + +static int match(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const void *matchinfo, + int offset, const void *hdr, u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_dscp_info *info = matchinfo; + const struct iphdr *iph = skb->nh.iph; + + u_int8_t sh_dscp = ((info->dscp << IPT_DSCP_SHIFT) & IPT_DSCP_MASK); + + return ((iph->tos&IPT_DSCP_MASK) == sh_dscp) ^ info->invert; +} + +static int checkentry(const char *tablename, const struct ipt_ip *ip, + void *matchinfo, unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_dscp_info))) + return 0; + + return 1; +} + +static struct ipt_match dscp_match = { { NULL, NULL }, "dscp", &match, + &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&dscp_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&dscp_match); + +} + +module_init(init); +module_exit(fini); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_ecn.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_ecn.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_ecn.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_ecn.c Fri Nov 29 00:53:15 2002 @@ -0,0 +1,118 @@ +/* IP tables module for matching the value of the IPv4 and TCP ECN bits + * + * ipt_ecn.c,v 1.3 2002/05/29 15:09:00 laforge Exp + * + * (C) 2002 by Harald Welte + * + * This software is distributed under the terms GNU GPL v2 + */ + +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("IP tables ECN matching module"); +MODULE_LICENSE("GPL"); + +static inline int match_ip(const struct sk_buff *skb, + const struct iphdr *iph, + const struct ipt_ecn_info *einfo) +{ + return ((iph->tos&IPT_ECN_IP_MASK) == einfo->ip_ect); +} + +static inline int match_tcp(const struct sk_buff *skb, + const struct iphdr *iph, + const struct ipt_ecn_info *einfo) +{ + struct tcphdr *tcph = (void *)iph + iph->ihl*4; + + if (einfo->operation & IPT_ECN_OP_MATCH_ECE) { + if (einfo->invert & IPT_ECN_OP_MATCH_ECE) { + if (tcph->ece == 1) + return 0; + } else { + if (tcph->ece == 0) + return 0; + } + } + + if (einfo->operation & IPT_ECN_OP_MATCH_CWR) { + if (einfo->invert & IPT_ECN_OP_MATCH_CWR) { + if (tcph->cwr == 1) + return 0; + } else { + if (tcph->cwr == 0) + return 0; + } + } + + return 1; +} + +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_ecn_info *info = matchinfo; + const struct iphdr *iph = skb->nh.iph; + + if (info->operation & IPT_ECN_OP_MATCH_IP) + if (!match_ip(skb, iph, info)) + return 0; + + if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) { + if (iph->protocol != IPPROTO_TCP) + return 0; + if (!match_tcp(skb, iph, info)) + return 0; + } + + 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_ecn_info *info = matchinfo; + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_ecn_info))) + return 0; + + if (info->operation & IPT_ECN_OP_MATCH_MASK) + return 0; + + if (info->invert & IPT_ECN_OP_MATCH_MASK) + return 0; + + if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR) + && ip->proto != IPPROTO_TCP) { + printk(KERN_WARNING "ipt_ecn: can't match TCP bits in rule for" + " non-tcp packets\n"); + return 0; + } + + return 1; +} + +static struct ipt_match ecn_match = { { NULL, NULL }, "ecn", &match, + &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&ecn_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ecn_match); +} + +module_init(init); +module_exit(fini); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_helper.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_helper.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_helper.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_helper.c Fri Nov 29 00:53:15 2002 @@ -0,0 +1,112 @@ +/* + * iptables module to match on related connections + * (c) 2001 Martin Josefsson + * + * Released under the terms of GNU GPLv2. + * + * 19 Mar 2002 Harald Welte : + * - Port to newnat infrastructure + */ +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_helper_info *info = matchinfo; + struct ip_conntrack_expect *exp; + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + if (!ct) { + DEBUGP("ipt_helper: Eek! invalid conntrack?\n"); + return 0; + } + + if (!ct->master) { + DEBUGP("ipt_helper: conntrack %p has no master\n", ct); + return 0; + } + + exp = ct->master; + if (!exp->expectant) { + DEBUGP("ipt_helper: expectation %p without expectant !?!\n", + exp); + return 0; + } + + if (!exp->expectant->helper) { + DEBUGP("ipt_helper: master ct %p has no helper\n", + exp->expectant); + return 0; + } + + DEBUGP("master's name = %s , info->name = %s\n", + exp->expectant->helper->name, info->name); + + return !strncmp(exp->expectant->helper->name, info->name, + strlen(exp->expectant->helper->name)) ^ info->invert; +} + +static int check(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_helper_info *info = matchinfo; + + info->name[29] = '\0'; + + /* verify size */ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_helper_info))) + return 0; + + /* verify that we actually should match anything */ + if ( strlen(info->name) == 0 ) + return 0; + + return 1; +} + +static struct ipt_match helper_match += { { NULL, NULL }, "helper", &match, &check, NULL, THIS_MODULE }; + +static int __init init(void) +{ + /* NULL if ip_conntrack not a module */ + if (ip_conntrack_module) + __MOD_INC_USE_COUNT(ip_conntrack_module); + return ipt_register_match(&helper_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&helper_match); + if (ip_conntrack_module) + __MOD_DEC_USE_COUNT(ip_conntrack_module); +} + +module_init(init); +module_exit(fini); + diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_owner.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_owner.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_owner.c Sun Sep 30 21:26:08 2001 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_owner.c Fri Nov 29 00:53:15 2002 @@ -12,6 +12,38 @@ #include static int +match_comm(const struct sk_buff *skb, const char *comm) +{ + struct task_struct *p; + struct files_struct *files; + int i; + + read_lock(&tasklist_lock); + for_each_task(p) { + if(strncmp(p->comm, comm, sizeof(p->comm))) + continue; + + task_lock(p); + files = p->files; + if(files) { + read_lock(&files->file_lock); + for (i=0; i < files->max_fds; i++) { + if (fcheck_files(files, i) == skb->sk->socket->file) { + read_unlock(&files->file_lock); + task_unlock(p); + read_unlock(&tasklist_lock); + return 1; + } + } + read_unlock(&files->file_lock); + } + task_unlock(p); + } + read_unlock(&tasklist_lock); + return 0; +} + +static int match_pid(const struct sk_buff *skb, pid_t pid) { struct task_struct *p; @@ -112,6 +144,12 @@ if(info->match & IPT_OWNER_SID) { if (!match_sid(skb, info->sid) ^ !!(info->invert & IPT_OWNER_SID)) + return 0; + } + + if(info->match & IPT_OWNER_COMM) { + if (!match_comm(skb, info->comm) ^ + !!(info->invert & IPT_OWNER_COMM)) return 0; } diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_pkttype.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_pkttype.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_pkttype.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_pkttype.c Fri Nov 29 00:53:15 2002 @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +static int match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_pkttype_info *info = matchinfo; + + return (skb->pkt_type == info->pkttype) ^ info->invert; +} + +static int checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ +/* + if (hook_mask + & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_IN) + | (1 << NF_IP_FORWARD))) { + printk("ipt_pkttype: only valid for PRE_ROUTING, LOCAL_IN or FORWARD.\n"); + return 0; + } +*/ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_pkttype_info))) + return 0; + + return 1; +} + +static struct ipt_match pkttype_match += { { NULL, NULL }, "pkttype", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&pkttype_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&pkttype_match); +} + +module_init(init); +module_exit(fini); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv4/netfilter/ipt_unclean.c linux-2.4.20-plain/net/ipv4/netfilter/ipt_unclean.c --- linux-2.4.19-plain/net/ipv4/netfilter/ipt_unclean.c Fri Dec 21 18:42:05 2001 +++ linux-2.4.20-plain/net/ipv4/netfilter/ipt_unclean.c Fri Nov 29 00:53:15 2002 @@ -211,15 +211,14 @@ /* Bad checksum? Don't print, just say it's unclean. */ - if (!more_frags && !embedded + if (!more_frags && !embedded && udph->check && csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_UDP, csum_partial((char *)udph, datalen, 0)) != 0) return 0; - /* CHECK: Ports can't be zero. */ - if (!udph->source || !udph->dest) { - limpk("UDP zero ports %u/%u\n", - ntohs(udph->source), ntohs(udph->dest)); + /* CHECK: Destination port can't be zero. */ + if (!udph->dest) { + limpk("UDP zero destination port\n"); return 0; } diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv6/netfilter/Config.in linux-2.4.20-plain/net/ipv6/netfilter/Config.in --- linux-2.4.19-plain/net/ipv6/netfilter/Config.in Mon Feb 25 20:38:14 2002 +++ linux-2.4.20-plain/net/ipv6/netfilter/Config.in Fri Nov 29 00:53:15 2002 @@ -24,6 +24,10 @@ fi # dep_tristate ' MAC address match support' CONFIG_IP6_NF_MATCH_MAC $CONFIG_IP6_NF_IPTABLES dep_tristate ' netfilter MARK match support' CONFIG_IP6_NF_MATCH_MARK $CONFIG_IP6_NF_IPTABLES + dep_tristate ' Packet Length match support' CONFIG_IP6_NF_MATCH_LENGTH $CONFIG_IP6_NF_IPTABLES + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' EUI64 address check (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_EUI64 $CONFIG_IP6_NF_IPTABLES + fi # dep_tristate ' Multiple port match support' CONFIG_IP6_NF_MATCH_MULTIPORT $CONFIG_IP6_NF_IPTABLES # dep_tristate ' TOS match support' CONFIG_IP6_NF_MATCH_TOS $CONFIG_IP6_NF_IPTABLES # if [ "$CONFIG_IP6_NF_CONNTRACK" != "n" ]; then diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv6/netfilter/Makefile linux-2.4.20-plain/net/ipv6/netfilter/Makefile --- linux-2.4.19-plain/net/ipv6/netfilter/Makefile Mon Feb 25 20:38:14 2002 +++ linux-2.4.20-plain/net/ipv6/netfilter/Makefile Fri Nov 29 00:53:15 2002 @@ -15,7 +15,9 @@ obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o obj-$(CONFIG_IP6_NF_MATCH_LIMIT) += ip6t_limit.o obj-$(CONFIG_IP6_NF_MATCH_MARK) += ip6t_mark.o +obj-$(CONFIG_IP6_NF_MATCH_LENGTH) += ip6t_length.o obj-$(CONFIG_IP6_NF_MATCH_MAC) += ip6t_mac.o +obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o obj-$(CONFIG_IP6_NF_MATCH_MULTIPORT) += ip6t_multiport.o obj-$(CONFIG_IP6_NF_MATCH_OWNER) += ip6t_owner.o obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv6/netfilter/ip6_queue.c linux-2.4.20-plain/net/ipv6/netfilter/ip6_queue.c --- linux-2.4.19-plain/net/ipv6/netfilter/ip6_queue.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv6/netfilter/ip6_queue.c Fri Nov 29 00:53:15 2002 @@ -15,7 +15,7 @@ * real coder of this. * Few changes needed, mainly the hard_routing code and * the netlink socket protocol (we're NETLINK_IP6_FW). - * + * 2002-06-25: Code cleanup. [JM: ported cleanup over from ip_queue.c] */ #include #include @@ -26,18 +26,12 @@ #include #include #include -#include +#include #include #include #include #include #include - -/* We're still usign the following structs. No need to change them: */ -/* ipq_packet_msg */ -/* ipq_mode_msg */ -/* ipq_verdict_msg */ -/* ipq_peer_msg */ #include #include #include @@ -47,184 +41,289 @@ #define NET_IPQ_QMAX 2088 #define NET_IPQ_QMAX_NAME "ip6_queue_maxlen" -typedef struct ip6q_rt_info { +struct ipq_rt_info { struct in6_addr daddr; struct in6_addr saddr; -} ip6q_rt_info_t; +}; -typedef struct ip6q_queue_element { - struct list_head list; /* Links element into queue */ - int verdict; /* Current verdict */ - struct nf_info *info; /* Extra info from netfilter */ - struct sk_buff *skb; /* Packet inside */ - ip6q_rt_info_t rt_info; /* May need post-mangle routing */ -} ip6q_queue_element_t; - -typedef int (*ip6q_send_cb_t)(ip6q_queue_element_t *e); - -typedef struct ip6q_peer { - pid_t pid; /* PID of userland peer */ - unsigned char died; /* We think the peer died */ - unsigned char copy_mode; /* Copy packet as well as metadata? */ - size_t copy_range; /* Range past metadata to copy */ - ip6q_send_cb_t send; /* Callback for sending data to peer */ -} ip6q_peer_t; - -typedef struct ip6q_queue { - int len; /* Current queue len */ - int *maxlen; /* Maximum queue len, via sysctl */ - unsigned char flushing; /* If queue is being flushed */ - unsigned char terminate; /* If the queue is being terminated */ - struct list_head list; /* Head of packet queue */ - spinlock_t lock; /* Queue spinlock */ - ip6q_peer_t peer; /* Userland peer */ -} ip6q_queue_t; +struct ipq_queue_entry { + struct list_head list; + struct nf_info *info; + struct sk_buff *skb; + struct ipq_rt_info rt_info; +}; -/**************************************************************************** - * - * Packet queue - * - ****************************************************************************/ -/* Dequeue a packet if matched by cmp, or the next available if cmp is NULL */ -static ip6q_queue_element_t * -ip6q_dequeue(ip6q_queue_t *q, - int (*cmp)(ip6q_queue_element_t *, unsigned long), - unsigned long data) -{ - struct list_head *i; - - spin_lock_bh(&q->lock); - for (i = q->list.prev; i != &q->list; i = i->prev) { - ip6q_queue_element_t *e = (ip6q_queue_element_t *)i; - - if (!cmp || cmp(e, data)) { - list_del(&e->list); - q->len--; - spin_unlock_bh(&q->lock); - return e; - } +typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long); + +static unsigned char copy_mode = IPQ_COPY_NONE; +static unsigned int queue_maxlen = IPQ_QMAX_DEFAULT; +static rwlock_t queue_lock = RW_LOCK_UNLOCKED; +static int peer_pid; +static unsigned int copy_range; +static unsigned int queue_total; +static struct sock *ipqnl; +static LIST_HEAD(queue_list); +static DECLARE_MUTEX(ipqnl_sem); + +static void +ipq_issue_verdict(struct ipq_queue_entry *entry, int verdict) +{ + nf_reinject(entry->skb, entry->info, verdict); + kfree(entry); +} + +static inline int +__ipq_enqueue_entry(struct ipq_queue_entry *entry) +{ + if (queue_total >= queue_maxlen) { + if (net_ratelimit()) + printk(KERN_WARNING "ip6_queue: full at %d entries, " + "dropping packet(s).\n", queue_total); + return -ENOSPC; + } + list_add(&entry->list, &queue_list); + queue_total++; + return 0; +} + +/* + * Find and return a queued entry matched by cmpfn, or return the last + * entry if cmpfn is NULL. + */ +static inline struct ipq_queue_entry * +__ipq_find_entry(ipq_cmpfn cmpfn, unsigned long data) +{ + struct list_head *p; + + list_for_each_prev(p, &queue_list) { + struct ipq_queue_entry *entry = (struct ipq_queue_entry *)p; + + if (!cmpfn || cmpfn(entry, data)) + return entry; } - spin_unlock_bh(&q->lock); return NULL; } -/* Flush all packets */ -static void ip6q_flush(ip6q_queue_t *q) +static inline void +__ipq_dequeue_entry(struct ipq_queue_entry *entry) { - ip6q_queue_element_t *e; - - spin_lock_bh(&q->lock); - q->flushing = 1; - spin_unlock_bh(&q->lock); - while ((e = ip6q_dequeue(q, NULL, 0))) { - e->verdict = NF_DROP; - nf_reinject(e->skb, e->info, e->verdict); - kfree(e); - } - spin_lock_bh(&q->lock); - q->flushing = 0; - spin_unlock_bh(&q->lock); -} - -static ip6q_queue_t *ip6q_create_queue(nf_queue_outfn_t outfn, - ip6q_send_cb_t send_cb, - int *errp, int *sysctl_qmax) + list_del(&entry->list); + queue_total--; +} + +static inline struct ipq_queue_entry * +__ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data) { - int status; - ip6q_queue_t *q; + struct ipq_queue_entry *entry; - *errp = 0; - q = kmalloc(sizeof(ip6q_queue_t), GFP_KERNEL); - if (q == NULL) { - *errp = -ENOMEM; + entry = __ipq_find_entry(cmpfn, data); + if (entry == NULL) return NULL; + + __ipq_dequeue_entry(entry); + return entry; +} + + +static inline void +__ipq_flush(int verdict) +{ + struct ipq_queue_entry *entry; + + while ((entry = __ipq_find_dequeue_entry(NULL, 0))) + ipq_issue_verdict(entry, verdict); +} + +static inline int +__ipq_set_mode(unsigned char mode, unsigned int range) +{ + int status = 0; + + switch(mode) { + case IPQ_COPY_NONE: + case IPQ_COPY_META: + copy_mode = mode; + copy_range = 0; + break; + + case IPQ_COPY_PACKET: + copy_mode = mode; + copy_range = range; + if (copy_range > 0xFFFF) + copy_range = 0xFFFF; + break; + + default: + status = -EINVAL; + } - q->peer.pid = 0; - q->peer.died = 0; - q->peer.copy_mode = IPQ_COPY_NONE; - q->peer.copy_range = 0; - q->peer.send = send_cb; - q->len = 0; - q->maxlen = sysctl_qmax; - q->flushing = 0; - q->terminate = 0; - INIT_LIST_HEAD(&q->list); - spin_lock_init(&q->lock); - status = nf_register_queue_handler(PF_INET6, outfn, q); - if (status < 0) { - *errp = -EBUSY; - kfree(q); + return status; +} + +static inline void +__ipq_reset(void) +{ + peer_pid = 0; + __ipq_set_mode(IPQ_COPY_NONE, 0); + __ipq_flush(NF_DROP); +} + +static struct ipq_queue_entry * +ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data) +{ + struct ipq_queue_entry *entry; + + write_lock_bh(&queue_lock); + entry = __ipq_find_dequeue_entry(cmpfn, data); + write_unlock_bh(&queue_lock); + return entry; +} + +static void +ipq_flush(int verdict) +{ + write_lock_bh(&queue_lock); + __ipq_flush(verdict); + write_unlock_bh(&queue_lock); +} + +static struct sk_buff * +ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp) +{ + unsigned char *old_tail; + size_t size = 0; + size_t data_len = 0; + struct sk_buff *skb; + struct ipq_packet_msg *pmsg; + struct nlmsghdr *nlh; + + read_lock_bh(&queue_lock); + + switch (copy_mode) { + case IPQ_COPY_META: + case IPQ_COPY_NONE: + size = NLMSG_SPACE(sizeof(*pmsg)); + data_len = 0; + break; + + case IPQ_COPY_PACKET: + if (copy_range == 0 || copy_range > entry->skb->len) + data_len = entry->skb->len; + else + data_len = copy_range; + + size = NLMSG_SPACE(sizeof(*pmsg) + data_len); + break; + + default: + *errp = -EINVAL; + read_unlock_bh(&queue_lock); return NULL; } - return q; + + read_unlock_bh(&queue_lock); + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) + goto nlmsg_failure; + + old_tail= skb->tail; + nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh)); + pmsg = NLMSG_DATA(nlh); + memset(pmsg, 0, sizeof(*pmsg)); + + pmsg->packet_id = (unsigned long )entry; + pmsg->data_len = data_len; + pmsg->timestamp_sec = entry->skb->stamp.tv_sec; + pmsg->timestamp_usec = entry->skb->stamp.tv_usec; + pmsg->mark = entry->skb->nfmark; + pmsg->hook = entry->info->hook; + pmsg->hw_protocol = entry->skb->protocol; + + if (entry->info->indev) + strcpy(pmsg->indev_name, entry->info->indev->name); + else + pmsg->indev_name[0] = '\0'; + + if (entry->info->outdev) + strcpy(pmsg->outdev_name, entry->info->outdev->name); + else + pmsg->outdev_name[0] = '\0'; + + if (entry->info->indev && entry->skb->dev) { + pmsg->hw_type = entry->skb->dev->type; + if (entry->skb->dev->hard_header_parse) + pmsg->hw_addrlen = + entry->skb->dev->hard_header_parse(entry->skb, + pmsg->hw_addr); + } + + if (data_len) + memcpy(pmsg->payload, entry->skb->data, data_len); + + nlh->nlmsg_len = skb->tail - old_tail; + return skb; + +nlmsg_failure: + if (skb) + kfree_skb(skb); + *errp = -EINVAL; + printk(KERN_ERR "ip6_queue: error creating packet message\n"); + return NULL; } -static int ip6q_enqueue(ip6q_queue_t *q, - struct sk_buff *skb, struct nf_info *info) +static int +ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info, void *data) { - ip6q_queue_element_t *e; - int status; - - e = kmalloc(sizeof(*e), GFP_ATOMIC); - if (e == NULL) { - printk(KERN_ERR "ip6_queue: OOM in enqueue\n"); + int status = -EINVAL; + struct sk_buff *nskb; + struct ipq_queue_entry *entry; + + if (copy_mode == IPQ_COPY_NONE) + return -EAGAIN; + + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry == NULL) { + printk(KERN_ERR "ip6_queue: OOM in ipq_enqueue_packet()\n"); return -ENOMEM; } - e->verdict = NF_DROP; - e->info = info; - e->skb = skb; + entry->info = info; + entry->skb = skb; - if (e->info->hook == NF_IP_LOCAL_OUT) { + if (entry->info->hook == NF_IP_LOCAL_OUT) { struct ipv6hdr *iph = skb->nh.ipv6h; - e->rt_info.daddr = iph->daddr; - e->rt_info.saddr = iph->saddr; + entry->rt_info.daddr = iph->daddr; + entry->rt_info.saddr = iph->saddr; } - spin_lock_bh(&q->lock); - if (q->len >= *q->maxlen) { - spin_unlock_bh(&q->lock); - if (net_ratelimit()) - printk(KERN_WARNING "ip6_queue: full at %d entries, " - "dropping packet(s).\n", q->len); - goto free_drop; - } - if (q->flushing || q->peer.copy_mode == IPQ_COPY_NONE - || q->peer.pid == 0 || q->peer.died || q->terminate) { - spin_unlock_bh(&q->lock); - goto free_drop; - } - status = q->peer.send(e); - if (status > 0) { - list_add(&e->list, &q->list); - q->len++; - spin_unlock_bh(&q->lock); - return status; - } - spin_unlock_bh(&q->lock); - if (status == -ECONNREFUSED) { - printk(KERN_INFO "ip6_queue: peer %d died, " - "resetting state and flushing queue\n", q->peer.pid); - q->peer.died = 1; - q->peer.pid = 0; - q->peer.copy_mode = IPQ_COPY_NONE; - q->peer.copy_range = 0; - ip6q_flush(q); - } -free_drop: - kfree(e); - return -EBUSY; -} + nskb = ipq_build_packet_message(entry, &status); + if (nskb == NULL) + goto err_out_free; + + write_lock_bh(&queue_lock); + + if (!peer_pid) + goto err_out_unlock; -static void ip6q_destroy_queue(ip6q_queue_t *q) -{ - nf_unregister_queue_handler(PF_INET6); - spin_lock_bh(&q->lock); - q->terminate = 1; - spin_unlock_bh(&q->lock); - ip6q_flush(q); - kfree(q); + status = netlink_unicast(ipqnl, nskb, peer_pid, MSG_DONTWAIT); + if (status < 0) + goto err_out_unlock; + + status = __ipq_enqueue_entry(entry); + if (status < 0) + goto err_out_unlock; + + write_unlock_bh(&queue_lock); + return status; + +err_out_unlock: + write_unlock_bh(&queue_lock); + +err_out_free: + kfree(entry); + return status; } /* @@ -236,7 +335,8 @@ * * If that one is modified, this one should be modified too. */ -static int route6_me_harder(struct sk_buff *skb) +static int +route6_me_harder(struct sk_buff *skb) { struct ipv6hdr *iph = skb->nh.ipv6h; struct dst_entry *dst; @@ -264,7 +364,9 @@ skb->dst = dst; return 0; } -static int ip6q_mangle_ipv6(ipq_verdict_msg_t *v, ip6q_queue_element_t *e) + +static int +ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct ipq_queue_entry *e) { int diff; struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload; @@ -306,357 +408,262 @@ */ if (e->info->hook == NF_IP_LOCAL_OUT) { struct ipv6hdr *iph = e->skb->nh.ipv6h; - if (!( iph->daddr.in6_u.u6_addr32[0] == e->rt_info.daddr.in6_u.u6_addr32[0] - && iph->daddr.in6_u.u6_addr32[1] == e->rt_info.daddr.in6_u.u6_addr32[1] - && iph->daddr.in6_u.u6_addr32[2] == e->rt_info.daddr.in6_u.u6_addr32[2] - && iph->daddr.in6_u.u6_addr32[3] == e->rt_info.daddr.in6_u.u6_addr32[3] - && iph->saddr.in6_u.u6_addr32[0] == e->rt_info.saddr.in6_u.u6_addr32[0] - && iph->saddr.in6_u.u6_addr32[1] == e->rt_info.saddr.in6_u.u6_addr32[1] - && iph->saddr.in6_u.u6_addr32[2] == e->rt_info.saddr.in6_u.u6_addr32[2] - && iph->saddr.in6_u.u6_addr32[3] == e->rt_info.saddr.in6_u.u6_addr32[3])) + if (ipv6_addr_cmp(&iph->daddr, &e->rt_info.daddr) || + ipv6_addr_cmp(&iph->saddr, &e->rt_info.saddr)) return route6_me_harder(e->skb); } return 0; } -static inline int id_cmp(ip6q_queue_element_t *e, unsigned long id) +static inline int +id_cmp(struct ipq_queue_entry *e, unsigned long id) { return (id == (unsigned long )e); } -static int ip6q_set_verdict(ip6q_queue_t *q, - ipq_verdict_msg_t *v, unsigned int len) +static int +ipq_set_verdict(struct ipq_verdict_msg *vmsg, unsigned int len) { - ip6q_queue_element_t *e; + struct ipq_queue_entry *entry; - if (v->value > NF_MAX_VERDICT) + if (vmsg->value > NF_MAX_VERDICT) return -EINVAL; - e = ip6q_dequeue(q, id_cmp, v->id); - if (e == NULL) + + entry = ipq_find_dequeue_entry(id_cmp, vmsg->id); + if (entry == NULL) return -ENOENT; else { - e->verdict = v->value; - if (v->data_len && v->data_len == len) - if (ip6q_mangle_ipv6(v, e) < 0) - e->verdict = NF_DROP; - nf_reinject(e->skb, e->info, e->verdict); - kfree(e); + int verdict = vmsg->value; + + if (vmsg->data_len && vmsg->data_len == len) + if (ipq_mangle_ipv6(vmsg, entry) < 0) + verdict = NF_DROP; + + ipq_issue_verdict(entry, verdict); return 0; } } -static int ip6q_receive_peer(ip6q_queue_t* q, ipq_peer_msg_t *m, - unsigned char type, unsigned int len) +static int +ipq_set_mode(unsigned char mode, unsigned int range) { + int status; + + write_lock_bh(&queue_lock); + status = __ipq_set_mode(mode, range); + write_unlock_bh(&queue_lock); + return status; +} +static int +ipq_receive_peer(struct ipq_peer_msg *pmsg, + unsigned char type, unsigned int len) +{ int status = 0; - int busy; - - spin_lock_bh(&q->lock); - busy = (q->terminate || q->flushing); - spin_unlock_bh(&q->lock); - if (busy) - return -EBUSY; - if (len < sizeof(ipq_peer_msg_t)) + + if (len < sizeof(*pmsg)) return -EINVAL; + switch (type) { - case IPQM_MODE: - switch (m->msg.mode.value) { - case IPQ_COPY_META: - q->peer.copy_mode = IPQ_COPY_META; - q->peer.copy_range = 0; - break; - case IPQ_COPY_PACKET: - q->peer.copy_mode = IPQ_COPY_PACKET; - q->peer.copy_range = m->msg.mode.range; - if (q->peer.copy_range > 0xFFFF) - q->peer.copy_range = 0xFFFF; - break; - default: - status = -EINVAL; - } - break; - case IPQM_VERDICT: - if (m->msg.verdict.value > NF_MAX_VERDICT) - status = -EINVAL; - else - status = ip6q_set_verdict(q, - &m->msg.verdict, - len - sizeof(*m)); + case IPQM_MODE: + status = ipq_set_mode(pmsg->msg.mode.value, + pmsg->msg.mode.range); + break; + + case IPQM_VERDICT: + if (pmsg->msg.verdict.value > NF_MAX_VERDICT) + status = -EINVAL; + else + status = ipq_set_verdict(&pmsg->msg.verdict, + len - sizeof(*pmsg)); break; - default: - status = -EINVAL; + default: + status = -EINVAL; } return status; } -static inline int dev_cmp(ip6q_queue_element_t *e, unsigned long ifindex) +static int +dev_cmp(struct ipq_queue_entry *entry, unsigned long ifindex) { - if (e->info->indev) - if (e->info->indev->ifindex == ifindex) + if (entry->info->indev) + if (entry->info->indev->ifindex == ifindex) return 1; - if (e->info->outdev) - if (e->info->outdev->ifindex == ifindex) + + if (entry->info->outdev) + if (entry->info->outdev->ifindex == ifindex) return 1; + return 0; } -/* Drop any queued packets associated with device ifindex */ -static void ip6q_dev_drop(ip6q_queue_t *q, int ifindex) +static void +ipq_dev_drop(int ifindex) { - ip6q_queue_element_t *e; + struct ipq_queue_entry *entry; - while ((e = ip6q_dequeue(q, dev_cmp, ifindex))) { - e->verdict = NF_DROP; - nf_reinject(e->skb, e->info, e->verdict); - kfree(e); - } -} - -/**************************************************************************** - * - * Netfilter interface - * - ****************************************************************************/ - -/* - * Packets arrive here from netfilter for queuing to userspace. - * All of them must be fed back via nf_reinject() or Alexey will kill Rusty. - */ -static int netfilter6_receive(struct sk_buff *skb, - struct nf_info *info, void *data) -{ - return ip6q_enqueue((ip6q_queue_t *)data, skb, info); -} - -/**************************************************************************** - * - * Netlink interface. - * - ****************************************************************************/ - -static struct sock *nfnl = NULL; -/* This is not a static one, so we should not repeat its name */ -ip6q_queue_t *nlq6 = NULL; - -static struct sk_buff *netlink_build_message(ip6q_queue_element_t *e, int *errp) -{ - unsigned char *old_tail; - size_t size = 0; - size_t data_len = 0; - struct sk_buff *skb; - ipq_packet_msg_t *pm; - struct nlmsghdr *nlh; - - switch (nlq6->peer.copy_mode) { - size_t copy_range; - - case IPQ_COPY_META: - size = NLMSG_SPACE(sizeof(*pm)); - data_len = 0; - break; - case IPQ_COPY_PACKET: - copy_range = nlq6->peer.copy_range; - if (copy_range == 0 || copy_range > e->skb->len) - data_len = e->skb->len; - else - data_len = copy_range; - size = NLMSG_SPACE(sizeof(*pm) + data_len); - - break; - case IPQ_COPY_NONE: - default: - *errp = -EINVAL; - return NULL; - } - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) - goto nlmsg_failure; - old_tail = skb->tail; - nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh)); - pm = NLMSG_DATA(nlh); - memset(pm, 0, sizeof(*pm)); - pm->packet_id = (unsigned long )e; - pm->data_len = data_len; - pm->timestamp_sec = e->skb->stamp.tv_sec; - pm->timestamp_usec = e->skb->stamp.tv_usec; - pm->mark = e->skb->nfmark; - pm->hook = e->info->hook; - if (e->info->indev) strcpy(pm->indev_name, e->info->indev->name); - else pm->indev_name[0] = '\0'; - if (e->info->outdev) strcpy(pm->outdev_name, e->info->outdev->name); - else pm->outdev_name[0] = '\0'; - pm->hw_protocol = e->skb->protocol; - if (e->info->indev && e->skb->dev) { - pm->hw_type = e->skb->dev->type; - if (e->skb->dev->hard_header_parse) - pm->hw_addrlen = - e->skb->dev->hard_header_parse(e->skb, - pm->hw_addr); - } - if (data_len) - memcpy(pm->payload, e->skb->data, data_len); - nlh->nlmsg_len = skb->tail - old_tail; - NETLINK_CB(skb).dst_groups = 0; - return skb; -nlmsg_failure: - if (skb) - kfree_skb(skb); - *errp = 0; - printk(KERN_ERR "ip6_queue: error creating netlink message\n"); - return NULL; -} - -static int netlink_send_peer(ip6q_queue_element_t *e) -{ - int status = 0; - struct sk_buff *skb; - - skb = netlink_build_message(e, &status); - if (skb == NULL) - return status; - return netlink_unicast(nfnl, skb, nlq6->peer.pid, MSG_DONTWAIT); + while ((entry = ipq_find_dequeue_entry(dev_cmp, ifindex)) != NULL) + ipq_issue_verdict(entry, NF_DROP); } #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) -static __inline__ void netlink_receive_user_skb(struct sk_buff *skb) +static inline void +ipq_rcv_skb(struct sk_buff *skb) { - int status, type; + int status, type, pid, flags, nlmsglen, skblen; struct nlmsghdr *nlh; - if (skb->len < sizeof(struct nlmsghdr)) + skblen = skb->len; + if (skblen < sizeof(*nlh)) return; nlh = (struct nlmsghdr *)skb->data; - if (nlh->nlmsg_len < sizeof(struct nlmsghdr) - || skb->len < nlh->nlmsg_len) - return; - - if(nlh->nlmsg_pid <= 0 - || !(nlh->nlmsg_flags & NLM_F_REQUEST) - || nlh->nlmsg_flags & NLM_F_MULTI) + nlmsglen = nlh->nlmsg_len; + if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen) + return; + + pid = nlh->nlmsg_pid; + flags = nlh->nlmsg_flags; + + if(pid <= 0 || !(flags & NLM_F_REQUEST) || flags & NLM_F_MULTI) RCV_SKB_FAIL(-EINVAL); - if (nlh->nlmsg_flags & MSG_TRUNC) + + if (flags & MSG_TRUNC) RCV_SKB_FAIL(-ECOMM); + type = nlh->nlmsg_type; if (type < NLMSG_NOOP || type >= IPQM_MAX) RCV_SKB_FAIL(-EINVAL); + if (type <= IPQM_BASE) return; + if(!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) RCV_SKB_FAIL(-EPERM); - if (nlq6->peer.pid && !nlq6->peer.died - && (nlq6->peer.pid != nlh->nlmsg_pid)) { - printk(KERN_WARNING "ip6_queue: peer pid changed from %d to " - "%d, flushing queue\n", nlq6->peer.pid, nlh->nlmsg_pid); - ip6q_flush(nlq6); - } - nlq6->peer.pid = nlh->nlmsg_pid; - nlq6->peer.died = 0; - status = ip6q_receive_peer(nlq6, NLMSG_DATA(nlh), - type, skb->len - NLMSG_LENGTH(0)); + + write_lock_bh(&queue_lock); + + if (peer_pid) { + if (peer_pid != pid) { + write_unlock_bh(&queue_lock); + RCV_SKB_FAIL(-EBUSY); + } + } + else + peer_pid = pid; + + write_unlock_bh(&queue_lock); + + status = ipq_receive_peer(NLMSG_DATA(nlh), type, + skblen - NLMSG_LENGTH(0)); if (status < 0) RCV_SKB_FAIL(status); - if (nlh->nlmsg_flags & NLM_F_ACK) + + if (flags & NLM_F_ACK) netlink_ack(skb, nlh, 0); return; } -/* Note: we are only dealing with single part messages at the moment. */ -static void netlink_receive_user_sk(struct sock *sk, int len) +static void +ipq_rcv_sk(struct sock *sk, int len) { do { struct sk_buff *skb; - if (rtnl_shlock_nowait()) + if (down_trylock(&ipqnl_sem)) return; + while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { - netlink_receive_user_skb(skb); + ipq_rcv_skb(skb); kfree_skb(skb); } - up(&rtnl_sem); - } while (nfnl && nfnl->receive_queue.qlen); -} + + up(&ipqnl_sem); -/**************************************************************************** - * - * System events - * - ****************************************************************************/ + } while (ipqnl && ipqnl->receive_queue.qlen); +} -static int receive_event(struct notifier_block *this, - unsigned long event, void *ptr) +static int +ipq_rcv_dev_event(struct notifier_block *this, + unsigned long event, void *ptr) { struct net_device *dev = ptr; /* Drop any packets associated with the downed device */ if (event == NETDEV_DOWN) - ip6q_dev_drop(nlq6, dev->ifindex); + ipq_dev_drop(dev->ifindex); return NOTIFY_DONE; } -struct notifier_block ip6q_dev_notifier = { - receive_event, +static struct notifier_block ipq_dev_notifier = { + ipq_rcv_dev_event, NULL, 0 }; -/**************************************************************************** - * - * Sysctl - queue tuning. - * - ****************************************************************************/ +static int +ipq_rcv_nl_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct netlink_notify *n = ptr; + + if (event == NETLINK_URELEASE && + n->protocol == NETLINK_IP6_FW && n->pid) { + write_lock_bh(&queue_lock); + if (n->pid == peer_pid) + __ipq_reset(); + write_unlock_bh(&queue_lock); + } + return NOTIFY_DONE; +} -static int sysctl_maxlen = IPQ_QMAX_DEFAULT; +static struct notifier_block ipq_nl_notifier = { + ipq_rcv_nl_event, + NULL, + 0 +}; -static struct ctl_table_header *ip6q_sysctl_header; +static int sysctl_maxlen = IPQ_QMAX_DEFAULT; +static struct ctl_table_header *ipq_sysctl_header; -static ctl_table ip6q_table[] = { +static ctl_table ipq_table[] = { { NET_IPQ_QMAX, NET_IPQ_QMAX_NAME, &sysctl_maxlen, sizeof(sysctl_maxlen), 0644, NULL, proc_dointvec }, { 0 } }; -static ctl_table ip6q_dir_table[] = { - {NET_IPV6, "ipv6", NULL, 0, 0555, ip6q_table, 0, 0, 0, 0, 0}, +static ctl_table ipq_dir_table[] = { + {NET_IPV6, "ipv6", NULL, 0, 0555, ipq_table, 0, 0, 0, 0, 0}, { 0 } }; -static ctl_table ip6q_root_table[] = { - {CTL_NET, "net", NULL, 0, 0555, ip6q_dir_table, 0, 0, 0, 0, 0}, +static ctl_table ipq_root_table[] = { + {CTL_NET, "net", NULL, 0, 0555, ipq_dir_table, 0, 0, 0, 0, 0}, { 0 } }; -/**************************************************************************** - * - * Procfs - debugging info. - * - ****************************************************************************/ - -static int ip6q_get_info(char *buffer, char **start, off_t offset, int length) +static int +ipq_get_info(char *buffer, char **start, off_t offset, int length) { int len; - spin_lock_bh(&nlq6->lock); + read_lock_bh(&queue_lock); + len = sprintf(buffer, - "Peer pid : %d\n" - "Peer died : %d\n" - "Peer copy mode : %d\n" - "Peer copy range : %Zu\n" - "Queue length : %d\n" - "Queue max. length : %d\n" - "Queue flushing : %d\n" - "Queue terminate : %d\n", - nlq6->peer.pid, - nlq6->peer.died, - nlq6->peer.copy_mode, - nlq6->peer.copy_range, - nlq6->len, - *nlq6->maxlen, - nlq6->flushing, - nlq6->terminate); - spin_unlock_bh(&nlq6->lock); + "Peer PID : %d\n" + "Copy mode : %hu\n" + "Copy range : %u\n" + "Queue length : %u\n" + "Queue max. length : %u\n", + peer_pid, + copy_mode, + copy_range, + queue_total, + queue_maxlen); + + read_unlock_bh(&queue_lock); + *start = buffer + offset; len -= offset; if (len > length) @@ -666,52 +673,70 @@ return len; } -/**************************************************************************** - * - * Module stuff. - * - ****************************************************************************/ - -static int __init init(void) +static int +init_or_cleanup(int init) { - int status = 0; + int status = -ENOMEM; struct proc_dir_entry *proc; - /* We must create the NETLINK_IP6_FW protocol service */ - nfnl = netlink_kernel_create(NETLINK_IP6_FW, netlink_receive_user_sk); - if (nfnl == NULL) { - printk(KERN_ERR "ip6_queue: initialisation failed: unable to " - "create kernel netlink socket\n"); - return -ENOMEM; + if (!init) + goto cleanup; + + netlink_register_notifier(&ipq_nl_notifier); + ipqnl = netlink_kernel_create(NETLINK_IP6_FW, ipq_rcv_sk); + if (ipqnl == NULL) { + printk(KERN_ERR "ip6_queue: failed to create netlink socket\n"); + goto cleanup_netlink_notifier; } - nlq6 = ip6q_create_queue(netfilter6_receive, - netlink_send_peer, &status, &sysctl_maxlen); - if (nlq6 == NULL) { - printk(KERN_ERR "ip6_queue: initialisation failed: unable to " - "create queue\n"); - sock_release(nfnl->socket); - return status; - } - /* The file will be /proc/net/ip6_queue */ - proc = proc_net_create(IPQ_PROC_FS_NAME, 0, ip6q_get_info); - if (proc) proc->owner = THIS_MODULE; + + proc = proc_net_create(IPQ_PROC_FS_NAME, 0, ipq_get_info); + if (proc) + proc->owner = THIS_MODULE; else { - ip6q_destroy_queue(nlq6); - sock_release(nfnl->socket); - return -ENOMEM; + printk(KERN_ERR "ip6_queue: failed to create proc entry\n"); + goto cleanup_ipqnl; + } + + register_netdevice_notifier(&ipq_dev_notifier); + ipq_sysctl_header = register_sysctl_table(ipq_root_table, 0); + + status = nf_register_queue_handler(PF_INET6, ipq_enqueue_packet, NULL); + if (status < 0) { + printk(KERN_ERR "ip6_queue: failed to register queue handler\n"); + goto cleanup_sysctl; } - register_netdevice_notifier(&ip6q_dev_notifier); - ip6q_sysctl_header = register_sysctl_table(ip6q_root_table, 0); + return status; + +cleanup: + nf_unregister_queue_handler(PF_INET6); + br_write_lock_bh(BR_NETPROTO_LOCK); + br_write_unlock_bh(BR_NETPROTO_LOCK); + ipq_flush(NF_DROP); + +cleanup_sysctl: + unregister_sysctl_table(ipq_sysctl_header); + unregister_netdevice_notifier(&ipq_dev_notifier); + proc_net_remove(IPQ_PROC_FS_NAME); + +cleanup_ipqnl: + sock_release(ipqnl->socket); + down(&ipqnl_sem); + up(&ipqnl_sem); + +cleanup_netlink_notifier: + netlink_unregister_notifier(&ipq_nl_notifier); return status; } +static int __init init(void) +{ + + return init_or_cleanup(1); +} + static void __exit fini(void) { - unregister_sysctl_table(ip6q_sysctl_header); - proc_net_remove(IPQ_PROC_FS_NAME); - unregister_netdevice_notifier(&ip6q_dev_notifier); - ip6q_destroy_queue(nlq6); - sock_release(nfnl->socket); + init_or_cleanup(0); } MODULE_DESCRIPTION("IPv6 packet queue handler"); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv6/netfilter/ip6_tables.c linux-2.4.20-plain/net/ipv6/netfilter/ip6_tables.c --- linux-2.4.19-plain/net/ipv6/netfilter/ip6_tables.c Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-plain/net/ipv6/netfilter/ip6_tables.c Fri Nov 29 00:53:15 2002 @@ -7,6 +7,8 @@ * 19 Jan 2002 Harald Welte * - increase module usage count as soon as we have rules inside * a table + * 06 Jun 2002 Andras Kis-Szabo + * - new extension header parser code */ #include #include @@ -25,6 +27,7 @@ #include #define IPV6_HDR_LEN (sizeof(struct ipv6hdr)) +#define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr)) /*#define DEBUG_IP_FIREWALL*/ /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */ @@ -133,43 +136,23 @@ return 0; } -/* takes in current header and pointer to the header */ -/* if another header exists, sets hdrptr to the next header - and returns the new header value, else returns 0 */ -static u_int8_t ip6_nexthdr(u_int8_t currenthdr, u_int8_t *hdrptr) -{ - int i; - u_int8_t hdrlen, nexthdr = 0; - switch(currenthdr){ - case IPPROTO_AH: - /* whoever decided to do the length of AUTH for ipv6 - in 32bit units unlike other headers should be beaten... - repeatedly...with a large stick...no, an even LARGER - stick...no, you're still not thinking big enough */ - nexthdr = *hdrptr; - hdrlen = hdrptr[i] * 4 + 8; - hdrptr = hdrptr + hdrlen; - break; - /*stupid rfc2402 */ - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - case IPPROTO_HOPOPTS: - nexthdr = *hdrptr; - hdrlen = hdrptr[1] * 8 + 8; - hdrptr = hdrptr + hdrlen; - break; - case IPPROTO_FRAGMENT: - nexthdr = *hdrptr; - hdrptr = hdrptr + 8; - break; - } - return nexthdr; - +/* Check for an extension */ +int +ip6t_ext_hdr(u8 nexthdr) +{ + return ( (nexthdr == IPPROTO_HOPOPTS) || + (nexthdr == IPPROTO_ROUTING) || + (nexthdr == IPPROTO_FRAGMENT) || + (nexthdr == IPPROTO_ESP) || + (nexthdr == IPPROTO_AH) || + (nexthdr == IPPROTO_NONE) || + (nexthdr == IPPROTO_DSTOPTS) ); } /* Returns whether matches rule or not. */ static inline int -ip6_packet_match(const struct ipv6hdr *ipv6, +ip6_packet_match(const struct sk_buff *skb, + const struct ipv6hdr *ipv6, const char *indev, const char *outdev, const struct ip6t_ip6 *ip6info, @@ -227,17 +210,58 @@ /* look for the desired protocol header */ if((ip6info->flags & IP6T_F_PROTO)) { u_int8_t currenthdr = ipv6->nexthdr; - u_int8_t *hdrptr; - hdrptr = (u_int8_t *)(ipv6 + 1); - do { - if (ip6info->proto == currenthdr) { - if(ip6info->invflags & IP6T_INV_PROTO) - return 0; - return 1; + struct ipv6_opt_hdr *hdrptr; + u_int16_t ptr; /* Header offset in skb */ + u_int16_t hdrlen; /* Header */ + + ptr = IPV6_HDR_LEN; + + while (ip6t_ext_hdr(currenthdr)) { + /* Is there enough space for the next ext header? */ + if (skb->len - ptr < IPV6_OPTHDR_LEN) + return 0; + + /* NONE or ESP: there isn't protocol part */ + /* If we want to count these packets in '-p all', + * we will change the return 0 to 1*/ + if ((currenthdr == IPPROTO_NONE) || + (currenthdr == IPPROTO_ESP)) + return 0; + + hdrptr = (struct ipv6_opt_hdr *)(skb->data + ptr); + + /* Size calculation */ + if (currenthdr == IPPROTO_FRAGMENT) { + hdrlen = 8; + } else if (currenthdr == IPPROTO_AH) + hdrlen = (hdrptr->hdrlen+2)<<2; + else + hdrlen = ipv6_optlen(hdrptr); + + currenthdr = hdrptr->nexthdr; + ptr += hdrlen; + /* ptr is too large */ + if ( ptr > skb->len ) + return 0; + } + + /* currenthdr contains the protocol header */ + + dprintf("Packet protocol %hi ?= %s%hi.\n", + currenthdr, + ip6info->invflags & IP6T_INV_PROTO ? "!":"", + ip6info->proto); + + if (ip6info->proto == currenthdr) { + if(ip6info->invflags & IP6T_INV_PROTO) { + return 0; } - currenthdr = ip6_nexthdr(currenthdr, hdrptr); - } while(currenthdr); - if (!(ip6info->invflags & IP6T_INV_PROTO)) + return 1; + } + + /* We need match for the '-p all', too! */ + if ((ip6info->proto != 0) && + !(ip6info->invflags & IP6T_INV_PROTO)) return 0; } return 1; @@ -360,7 +384,8 @@ IP_NF_ASSERT(e); IP_NF_ASSERT(back); (*pskb)->nfcache |= e->nfcache; - if (ip6_packet_match(ipv6, indev, outdev, &e->ipv6, offset)) { + if (ip6_packet_match(*pskb, ipv6, indev, outdev, + &e->ipv6, offset)) { struct ip6t_entry_target *t; if (IP6T_MATCH_ITERATE(e, do_match, @@ -743,7 +768,7 @@ t = ip6t_get_target(e); target = find_target_lock(t->u.user.name, &ret, &ip6t_mutex); if (!target) { - // duprintf("check_entry: `%s' not found\n", t->u.name); + duprintf("check_entry: `%s' not found\n", t->u.user.name); goto cleanup_matches; } if (target->me) @@ -1469,6 +1494,9 @@ duprintf("table->private->number = %u\n", table->private->number); + /* save number of initial entries */ + table->private->initial_entries = table->private->number; + table->lock = RW_LOCK_UNLOCKED; list_prepend(&ip6t_tables, table); @@ -1827,6 +1855,7 @@ EXPORT_SYMBOL(ip6t_unregister_match); EXPORT_SYMBOL(ip6t_register_target); EXPORT_SYMBOL(ip6t_unregister_target); +EXPORT_SYMBOL(ip6t_ext_hdr); module_init(init); module_exit(fini); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv6/netfilter/ip6t_LOG.c linux-2.4.20-plain/net/ipv6/netfilter/ip6t_LOG.c --- linux-2.4.19-plain/net/ipv6/netfilter/ip6t_LOG.c Mon Nov 5 18:53:07 2001 +++ linux-2.4.20-plain/net/ipv6/netfilter/ip6t_LOG.c Fri Nov 29 00:53:15 2002 @@ -112,7 +112,7 @@ printk("FRAG:%u ", ntohs(fhdr->frag_off) & 0xFFF8); /* Max length: 11 "INCOMPLETE " */ - if (fhdr->frag_off & __constant_htons(0x0001)) + if (fhdr->frag_off & htons(0x0001)) printk("INCOMPLETE "); printk("ID:%08x ", fhdr->identification); @@ -289,12 +289,39 @@ /* MAC logging for input chain only. */ printk("MAC="); if ((*pskb)->dev && (*pskb)->dev->hard_header_len && (*pskb)->mac.raw != (void*)ipv6h) { - int i; - unsigned char *p = (*pskb)->mac.raw; - for (i = 0; i < (*pskb)->dev->hard_header_len; i++,p++) + if ((*pskb)->dev->type != ARPHRD_SIT){ + int i; + unsigned char *p = (*pskb)->mac.raw; + for (i = 0; i < (*pskb)->dev->hard_header_len; i++,p++) printk("%02x%c", *p, - i==(*pskb)->dev->hard_header_len - 1 - ? ' ':':'); + i==(*pskb)->dev->hard_header_len - 1 + ? ' ':':'); + } else { + int i; + unsigned char *p = (*pskb)->mac.raw; + if ( p - (ETH_ALEN*2+2) > (*pskb)->head ){ + p -= (ETH_ALEN+2); + for (i = 0; i < (ETH_ALEN); i++,p++) + printk("%02x%s", *p, + i == ETH_ALEN-1 ? "->" : ":"); + p -= (ETH_ALEN*2); + for (i = 0; i < (ETH_ALEN); i++,p++) + printk("%02x%c", *p, + i == ETH_ALEN-1 ? ' ' : ':'); + } + + if (((*pskb)->dev->addr_len == 4) && + (*pskb)->dev->hard_header_len > 20){ + printk("TUNNEL="); + p = (*pskb)->mac.raw + 12; + for (i = 0; i < 4; i++,p++) + printk("%3d%s", *p, + i == 3 ? "->" : "."); + for (i = 0; i < 4; i++,p++) + printk("%3d%c", *p, + i == 3 ? ' ' : '.'); + } + } } else printk(" "); } diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv6/netfilter/ip6t_eui64.c linux-2.4.20-plain/net/ipv6/netfilter/ip6t_eui64.c --- linux-2.4.19-plain/net/ipv6/netfilter/ip6t_eui64.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/net/ipv6/netfilter/ip6t_eui64.c Fri Nov 29 00:53:15 2002 @@ -0,0 +1,89 @@ +/* Kernel module to match EUI64 address parameters. */ +#include +#include +#include +#include + +#include + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + + unsigned char eui64[8]; + int i=0; + + if ( !(skb->mac.raw >= skb->head + && (skb->mac.raw + ETH_HLEN) <= skb->data) + && offset != 0) { + *hotdrop = 1; + return 0; + } + + memset(eui64, 0, sizeof(eui64)); + + if (skb->mac.ethernet->h_proto == ntohs(ETH_P_IPV6)) { + if (skb->nh.ipv6h->version == 0x6) { + memcpy(eui64, skb->mac.ethernet->h_source, 3); + memcpy(eui64 + 5, skb->mac.ethernet->h_source + 3, 3); + eui64[3]=0xff; + eui64[4]=0xfe; + eui64[0] |= 0x02; + + i=0; + while ((skb->nh.ipv6h->saddr.in6_u.u6_addr8[8+i] == + eui64[i]) && (i<8)) i++; + + if ( i == 8 ) + return 1; + } + } + + return 0; +} + +static int +ip6t_eui64_checkentry(const char *tablename, + const struct ip6t_ip6 *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (hook_mask + & ~((1 << NF_IP6_PRE_ROUTING) | (1 << NF_IP6_LOCAL_IN) | + (1 << NF_IP6_PRE_ROUTING) )) { + printk("ip6t_eui64: only valid for PRE_ROUTING, LOCAL_IN or FORWARD.\n"); + return 0; + } + + if (matchsize != IP6T_ALIGN(sizeof(int))) + return 0; + + return 1; +} + +static struct ip6t_match eui64_match += { { NULL, NULL }, "eui64", &match, &ip6t_eui64_checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ip6t_register_match(&eui64_match); +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&eui64_match); +} + +module_init(init); +module_exit(fini); +MODULE_DESCRIPTION("IPv6 EUI64 address checking match"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andras Kis-Szabo "); diff -Nru --exclude .depend --exclude *.o --exclude *.ver --exclude .*.flags --exclude *.orig --exclude *.rej --exclude *~ linux-2.4.19-plain/net/ipv6/netfilter/ip6t_length.c linux-2.4.20-plain/net/ipv6/netfilter/ip6t_length.c --- linux-2.4.19-plain/net/ipv6/netfilter/ip6t_length.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-plain/net/ipv6/netfilter/ip6t_length.c Fri Nov 29 00:53:15 2002 @@ -0,0 +1,51 @@ +/* Length Match - IPv6 Port */ + +#include +#include +#include +#include + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ip6t_length_info *info = matchinfo; + u_int16_t pktlen = ntohs(skb->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr); + + return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; +} + +static int +checkentry(const char *tablename, + const struct ip6t_ip6 *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_length_info))) + return 0; + + return 1; +} + +static struct ip6t_match length_match += { { NULL, NULL }, "length", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ip6t_register_match(&length_match); +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&length_match); +} + +module_init(init); +module_exit(fini);