[PATCH] alpha: stateless redirection & connection interception

Balazs Scheidler bazsi@balabit.hu
Mon, 2 Jul 2001 10:49:26 +0200


--UlVJffcvxoiEqYs2
Content-Type: text/plain; charset=us-ascii

Hi,

My first version of $SUBJECT is attached to this mail. It basically contains
the following:

- stateless redirection support, connection redirection especially tailored
  for proxy firewalls. No connection tracking is involved and requires
  support from different protocols. (TCP/UDP)
- connection interception support, this means that a user process can bind
  to any (even foreign) address, and if traffic is to be forwarded to the
  bound address it gets to the socket (THIS IS CURRENTLY BROKEN)

Here's how it works:
stateless redirection
  
  I have a mangle target named TPROXY, which sets IPCB(skb)->deliver_locally
  to TRUE, and IPCB(skb)->redir_port to the port where the transparent proxy
  is listening.

  I have registered netfilter hooks (currently with priority mangle+1) in
  the LOCAL_OUT and FORWARD chains, these hooks check deliver_locally and
  call ip_local_deliver_finish() for those packets (instead of
  ip_local_deliver() to avoid traversal of the INPUT chain)

  UDP/TCP protocol handlers check if redir_port is set, and if it is they
  look up sockets a bit differently.

connection interception

  this is not yet complete, however the way it'll work is already visible.
  A netfilter hook registered into the forward chain call protocol specific
  functions (tcp/udp/icmp) to check whether a to-be-forwarded packet matches
  a bound socket.  If it does, packet is delivered locally.

  TODO:
    * SO_FREEBIND can be called by a simple user process, we should check
      capable(CAP_NET_ADMIN)
    * a socket option which _must_ be enabled to receive such connections
      (so sockets used for interception must explicitly request such
      treatment)
    * a sysctl to complete turn interception processing off (to avoid socket
      lookup for every forwarded packet)

How you can try it:
-------------------

NOTE: this is strictly ALPHA code, and contains some debug messages, and can
also crash the kernel.

After applying the patch, configure your kernel as usual, and enable IP
connection interception, IP stateless redirection, and netfilter TPROXY
target. Compile/boot.

Load the ip_tproxy module, and add the following rule to the mangle table:

iptables -t mangle -A OUTPUT -p tcp -d 0/0 --dport 80 -j TPROXY --on-port 1999

start a listener on port 1999 (for example: nc -l -p 1999), and try
connecting to port 80. You can also add rules similar to this to your INPUT
chain. (but this is untested) make sure that no process is listening on port
80. (this is a consequence of how socket lookup works now, I'll give it a
thought)

Comments appreciated.

-- 
Bazsi

--UlVJffcvxoiEqYs2
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="linux-2.4.4-slessredir-intercept.diff"

diff -urN --exclude-from=kernel-exclude linux-2.4.4/include/linux/netfilter_ipv4/ipt_TPROXY.h linux-2.4.4-udp/include/linux/netfilter_ipv4/ipt_TPROXY.h
--- linux-2.4.4/include/linux/netfilter_ipv4/ipt_TPROXY.h	Thu Jan  1 01:00:00 1970
+++ linux-2.4.4-udp/include/linux/netfilter_ipv4/ipt_TPROXY.h	Sat Jun 23 10:07:52 2001
@@ -0,0 +1,8 @@
+#ifndef _IPT_TPROXY_H_target
+#define _IPT_TPROXY_H_target
+
+struct ipt_tproxy_target_info {
+	u_int16_t redir_port;
+};
+
+#endif /*_IPT_TPROXY_H_target*/
diff -urN --exclude-from=kernel-exclude linux-2.4.4/include/linux/netfilter_ipv4.h linux-2.4.4-udp/include/linux/netfilter_ipv4.h
--- linux-2.4.4/include/linux/netfilter_ipv4.h	Sat Apr 28 00:48:43 2001
+++ linux-2.4.4-udp/include/linux/netfilter_ipv4.h	Sun May 20 10:49:33 2001
@@ -72,5 +72,6 @@
 /* 2.2 firewalling (+ masq) went from 64 through 76 */
 /* 2.4 firewalling went 64 through 67. */
 #define SO_ORIGINAL_DST 80
+#define SO_ORIGINAL_DST_UDP 81
 
 #endif /*__LINUX_IP_NETFILTER_H*/
diff -urN --exclude-from=kernel-exclude linux-2.4.4/include/linux/udp.h linux-2.4.4-udp/include/linux/udp.h
--- linux-2.4.4/include/linux/udp.h	Sun Sep  7 23:00:24 1997
+++ linux-2.4.4-udp/include/linux/udp.h	Fri May 18 21:09:49 2001
@@ -25,5 +25,6 @@
 	__u16	check;
 };
 
+#define UDP_TPROXY		1
 
 #endif	/* _LINUX_UDP_H */
diff -urN --exclude-from=kernel-exclude linux-2.4.4/include/net/ip.h linux-2.4.4-udp/include/net/ip.h
--- linux-2.4.4/include/net/ip.h	Sat Apr 28 00:49:41 2001
+++ linux-2.4.4-udp/include/net/ip.h	Sat Jun 30 21:45:59 2001
@@ -46,6 +46,11 @@
 #define IPSKB_MASQUERADED	1
 #define IPSKB_TRANSLATED	2
 #define IPSKB_FORWARDED		4
+
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+	u8			deliver_locally;
+	u16			redirport;
+#endif
 };
 
 struct ipcm_cookie
@@ -89,6 +94,7 @@
 extern int		ip_rcv(struct sk_buff *skb, struct net_device *dev,
 			       struct packet_type *pt);
 extern int		ip_local_deliver(struct sk_buff *skb);
+extern int		ip_local_deliver_finish(struct sk_buff *skb);
 extern int		ip_mr_input(struct sk_buff *skb);
 extern int		ip_output(struct sk_buff *skb);
 extern int		ip_mc_output(struct sk_buff *skb);
diff -urN --exclude-from=kernel-exclude linux-2.4.4/include/net/route.h linux-2.4.4-udp/include/net/route.h
--- linux-2.4.4/include/net/route.h	Sat Apr 28 00:49:18 2001
+++ linux-2.4.4-udp/include/net/route.h	Fri May 18 22:42:45 2001
@@ -37,7 +37,7 @@
 #define RTO_ONLINK	0x01
 #define RTO_TPROXY	0x80000000
 
-#define RTO_CONN	0
+#define RTO_CONN	RTO_TPROXY
 
 struct rt_key
 {
diff -urN --exclude-from=kernel-exclude linux-2.4.4/include/net/tcp.h linux-2.4.4-udp/include/net/tcp.h
--- linux-2.4.4/include/net/tcp.h	Sat Apr 28 00:50:22 2001
+++ linux-2.4.4-udp/include/net/tcp.h	Sat Jun 30 21:46:26 2001
@@ -479,6 +479,9 @@
 	__u32			loc_addr;
 	__u32			rmt_addr;
 	struct ip_options	*opt;
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+	__u16			lcl_port;
+#endif
 };
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
diff -urN --exclude-from=kernel-exclude linux-2.4.4/net/ipv4/Config.in linux-2.4.4-udp/net/ipv4/Config.in
--- linux-2.4.4/net/ipv4/Config.in	Tue Aug 22 17:59:00 2000
+++ linux-2.4.4-udp/net/ipv4/Config.in	Thu Jun 14 13:05:38 2001
@@ -2,6 +2,8 @@
 # IP configuration
 #
 bool '  IP: multicasting' CONFIG_IP_MULTICAST
+bool '  IP: socket interception' CONFIG_IP_INTERCEPT
+bool '  IP: stateless redirection support' CONFIG_IP_STATELESS_REDIRECT
 bool '  IP: advanced router' CONFIG_IP_ADVANCED_ROUTER
 if [ "$CONFIG_IP_ADVANCED_ROUTER" = "y" ]; then
    define_bool CONFIG_RTNETLINK y	
diff -urN --exclude-from=kernel-exclude linux-2.4.4/net/ipv4/icmp.c linux-2.4.4-udp/net/ipv4/icmp.c
--- linux-2.4.4/net/ipv4/icmp.c	Thu Apr 19 17:38:50 2001
+++ linux-2.4.4-udp/net/ipv4/icmp.c	Thu Jun 14 15:47:24 2001
@@ -860,6 +860,72 @@
 {
 }
 
+#ifdef CONFIG_IP_INTERCEPT
+
+/*
+ *      Check incoming icmp packets not addressed locally, to check whether
+ *      they relate to a (proxying) socket on our system.
+ *      Needed for transparent proxying.
+ *
+ *      This code is presently ugly and needs cleanup.
+ *      Probably should add a chkaddr entry to ipprot to call a chk routine
+ *      in udp.c or tcp.c...
+ */
+
+/* This should work with the new hashes now. -DaveM */
+extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif);
+extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif);
+
+int icmp_chksock(struct sk_buff *skb)
+{
+        unsigned short size = skb->nh.iph->ihl*4;
+        unsigned short clen = ntohs(skb->nh.iph->tot_len) - size;
+        struct icmphdr *icmph = (struct icmphdr *)(skb->nh.raw + size);
+        struct iphdr *iph = (struct iphdr *) (icmph + 1);
+
+        if (clen < sizeof(struct icmphdr)) return 0;
+        if (icmph->type == ICMP_DEST_UNREACH || icmph->type == ICMP_REDIRECT) {
+                struct sock *sk;
+                
+                clen -= sizeof(struct icmphdr);
+                if (clen < sizeof(struct iphdr) ||
+                        clen < iph->ihl * 4 + sizeof(struct udphdr))
+                        return 0;
+                switch (iph->protocol) {
+                case IPPROTO_TCP:
+                        {
+                        struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
+
+                        sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source, skb->dev->ifindex);
+                        if (!sk || (sk->state == TCP_LISTEN))
+                                return 0;
+                        /*
+                         * This packet came from us.
+                         */
+                        return 1;
+                        }
+                case IPPROTO_UDP:
+                        {
+                        struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
+
+                        sk = udp_v4_lookup(iph->daddr, uh->dest, iph->saddr, uh->source, skb->dev->ifindex);
+                        if (!sk) return 0;
+                        if (sk->saddr != iph->saddr && inet_addr_type(iph->saddr) != RTN_LOCAL)
+                                return 0;
+                        /*
+                         * This packet may have come from us.
+                         * Assume it did.
+                         */
+                        return 1;
+                        }
+                }
+        }
+        return 0;
+}
+
+
+#endif
+
 /* 
  *	Deal with incoming ICMP packets.
  */
diff -urN --exclude-from=kernel-exclude linux-2.4.4/net/ipv4/ip_input.c linux-2.4.4-udp/net/ipv4/ip_input.c
--- linux-2.4.4/net/ipv4/ip_input.c	Thu Apr 12 21:11:39 2001
+++ linux-2.4.4-udp/net/ipv4/ip_input.c	Sat Jun 30 21:36:10 2001
@@ -216,7 +216,7 @@
 	return ret;
 }
 
-static inline int ip_local_deliver_finish(struct sk_buff *skb)
+inline int ip_local_deliver_finish(struct sk_buff *skb)
 {
 	int ihl = skb->nh.iph->ihl*4;
 
diff -urN --exclude-from=kernel-exclude linux-2.4.4/net/ipv4/netfilter/Config.in linux-2.4.4-udp/net/ipv4/netfilter/Config.in
--- linux-2.4.4/net/ipv4/netfilter/Config.in	Wed Mar  7 07:44:16 2001
+++ linux-2.4.4-udp/net/ipv4/netfilter/Config.in	Sat Jun 23 10:12:50 2001
@@ -59,6 +59,7 @@
   if [ "$CONFIG_IP_NF_MANGLE" != "n" ]; then
     dep_tristate '    TOS target support' CONFIG_IP_NF_TARGET_TOS $CONFIG_IP_NF_MANGLE
     dep_tristate '    MARK target support' CONFIG_IP_NF_TARGET_MARK $CONFIG_IP_NF_MANGLE
+    dep_tristate '    TPROXY target support' CONFIG_IP_NF_TARGET_TPROXY $CONFIG_IP_NF_MANGLE
   fi
   dep_tristate '  LOG target support' CONFIG_IP_NF_TARGET_LOG $CONFIG_IP_NF_IPTABLES
   dep_tristate '  TCPMSS target support' CONFIG_IP_NF_TARGET_TCPMSS $CONFIG_IP_NF_IPTABLES
diff -urN --exclude-from=kernel-exclude linux-2.4.4/net/ipv4/netfilter/Makefile linux-2.4.4-udp/net/ipv4/netfilter/Makefile
--- linux-2.4.4/net/ipv4/netfilter/Makefile	Thu Apr 26 00:00:28 2001
+++ linux-2.4.4-udp/net/ipv4/netfilter/Makefile	Sat Jun 23 18:14:52 2001
@@ -60,6 +60,7 @@
 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_TPROXY) += ipt_TPROXY.o ip_tproxy.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 -urN --exclude-from=kernel-exclude linux-2.4.4/net/ipv4/netfilter/ip_tproxy.c linux-2.4.4-udp/net/ipv4/netfilter/ip_tproxy.c
--- linux-2.4.4/net/ipv4/netfilter/ip_tproxy.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.4-udp/net/ipv4/netfilter/ip_tproxy.c	Sat Jun 30 21:54:49 2001
@@ -0,0 +1,123 @@
+#include <linux/version.h>
+#include <linux/module.h>
+#include <net/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+
+#if 0
+
+extern int icmp_chksock(struct sk_buff *);
+extern int tcp_chksock(struct sk_buff *);
+extern int udp_chksock(struct sk_buff *);
+
+int ip_chksock(struct sk_buff *skb)
+{
+        /* FIXME: check proc variable to enable connection interception */
+
+        switch (skb->nh.iph->protocol) {
+        case IPPROTO_ICMP:
+                return icmp_chksock(skb);
+        case IPPROTO_TCP:
+                return tcp_chksock(skb);
+        case IPPROTO_UDP:
+                return udp_chksock(skb);
+        default:
+                return 0;
+        }
+}
+#endif
+
+int ip_tproxy_redirect(struct sk_buff *skb)
+{
+        if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+		skb = ip_defrag(skb);
+                if (!skb)
+                	return 0;
+	}
+
+        if (!skb->dev)
+        	skb->dev = skb->dst->dev;
+
+	IPCB(skb)->deliver_locally = 0;
+        
+        ip_local_deliver_finish(skb);
+        return NF_STOLEN;
+}
+
+unsigned int ip_tproxy_fw(unsigned int hooknum,
+                          struct sk_buff **pskb,
+                          const struct net_device *in,
+                          const struct net_device *out,
+                          int (*okfn)(struct sk_buff *))
+{
+        if (IPCB(*pskb)->deliver_locally /* || ip_chksock(skb) */) {
+                printk(KERN_DEBUG "lokalis socket... 1\n");
+                return ip_tproxy_redirect(*pskb);
+        }
+
+	return NF_ACCEPT;
+}
+
+
+unsigned int ip_tproxy_lout(unsigned int hooknum,
+                            struct sk_buff **pskb,
+                            const struct net_device *in,
+                            const struct net_device *out,
+                            int (*okfn)(struct sk_buff *))
+{
+        printk(KERN_DEBUG "ip_tproxy_lout... 1 %p, %d\n", *pskb, IPCB(*pskb)->deliver_locally);
+        if (IPCB(*pskb)->deliver_locally /* || ip_chksock(skb) */) {
+                printk(KERN_DEBUG "lokalis socket... 2\n");
+                return ip_tproxy_redirect(*pskb);
+        }
+	return NF_ACCEPT;
+}
+
+/* we must run after mangle */
+static struct nf_hook_ops ip_tproxy_fw_ops
+= { { NULL, NULL }, ip_tproxy_fw, PF_INET, NF_IP_FORWARD,
+        NF_IP_PRI_MANGLE + 1 };
+static struct nf_hook_ops ip_tproxy_local_out_ops
+= { { NULL, NULL }, ip_tproxy_lout, PF_INET, NF_IP_LOCAL_OUT,
+        NF_IP_PRI_MANGLE + 1 };
+        
+ 
+static int init_or_cleanup(int startup)
+{
+	int ret = 0;
+	
+	if (!startup) {
+		goto clean_all;
+	}
+        ret = nf_register_hook(&ip_tproxy_fw_ops);
+        if (ret < 0) {
+        	printk("ip_tproxy: can't register forward hook.\n");
+        	goto clean_nothing;
+        }
+	ret = nf_register_hook(&ip_tproxy_local_out_ops);
+	if (ret < 0) {
+		printk("ip_tproxy: can't register local out hook.\n");
+		goto clean_fwops;
+	}
+	printk("ip_tproxy initialized\n");
+	return ret;
+clean_all:
+	nf_unregister_hook(&ip_tproxy_local_out_ops);
+clean_fwops:
+	nf_unregister_hook(&ip_tproxy_fw_ops);
+clean_nothing:
+	return ret;
+}
+        
+static int __init init(void)
+{
+        return init_or_cleanup(1);
+}
+        
+static void __exit fini(void)
+{
+	init_or_cleanup(0);
+}
+                
+module_init(init);
+module_exit(fini);
diff -urN --exclude-from=kernel-exclude linux-2.4.4/net/ipv4/netfilter/ipt_TPROXY.c linux-2.4.4-udp/net/ipv4/netfilter/ipt_TPROXY.c
--- linux-2.4.4/net/ipv4/netfilter/ipt_TPROXY.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.4-udp/net/ipv4/netfilter/ipt_TPROXY.c	Sun Jun 24 12:25:18 2001
@@ -0,0 +1,65 @@
+/* This is a module which is used for redirecting a connection to a transparent proxy */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <net/checksum.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_TPROXY.h>
+
+static unsigned int
+target(struct sk_buff **pskb,
+       unsigned int hooknum,
+       const struct net_device *in,
+       const struct net_device *out,
+       const void *targinfo,
+       void *userinfo)
+{
+	const struct ipt_tproxy_target_info *tproxyinfo = targinfo;
+
+	printk(KERN_DEBUG "ipt_TPROXY target, skb=%p\n", *pskb);
+	IPCB(*pskb)->deliver_locally = 1;
+        IPCB(*pskb)->redirport = tproxyinfo->redir_port;
+	return IPT_CONTINUE;
+}
+
+static int
+checkentry(const char *tablename,
+	   const struct ipt_entry *e,
+           void *targinfo,
+           unsigned int targinfosize,
+           unsigned int hook_mask)
+{
+	if (targinfosize != IPT_ALIGN(sizeof(struct ipt_tproxy_target_info))) {
+		printk(KERN_WARNING "TPROXY: targinfosize %u != %Zu\n",
+		       targinfosize,
+		       IPT_ALIGN(sizeof(struct ipt_tproxy_target_info)));
+		return 0;
+	}
+
+	if (strcmp(tablename, "mangle") != 0) {
+		printk(KERN_WARNING "TPROXY: can only be called from \"mangle\" table, not \"%s\"\n", tablename);
+		return 0;
+	}
+
+	return 1;
+}
+
+static struct ipt_target ipt_tproxy_reg
+= { { NULL, NULL }, "TPROXY", target, checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+	if (ipt_register_target(&ipt_tproxy_reg))
+		return -EINVAL;
+
+	return 0;
+}
+
+static void __exit fini(void)
+{
+	ipt_unregister_target(&ipt_tproxy_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff -urN --exclude-from=kernel-exclude linux-2.4.4/net/ipv4/route.c linux-2.4.4-udp/net/ipv4/route.c
--- linux-2.4.4/net/ipv4/route.c	Thu Apr 12 21:11:39 2001
+++ linux-2.4.4-udp/net/ipv4/route.c	Fri May 18 22:48:32 2001
@@ -1621,6 +1621,7 @@
 	int free_res = 0;
 	int err;
 	u32 tos;
+	u32 nochecksrc = (tos & RTO_TPROXY);
 
 	tos = oldkey->tos & (IPTOS_RT_MASK|RTO_ONLINK);
 	key.dst = oldkey->dst;
@@ -1645,8 +1646,11 @@
 
 		/* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
 		dev_out = ip_dev_find(oldkey->src);
-		if (dev_out == NULL)
-			return -EINVAL;
+		if (dev_out == NULL) {
+			if (nochecksrc || inet_addr_type(oldkey->src) != RTN_UNICAST)
+				return -EINVAL;
+			flags |= RTCF_TPROXY;
+		}
 
 		/* I removed check for oif == dev_out->oif here.
 		   It was wrong by three reasons:
@@ -1657,6 +1661,7 @@
 		 */
 
 		if (oldkey->oif == 0
+		    && dev_out
 		    && (MULTICAST(oldkey->dst) || oldkey->dst == 0xFFFFFFFF)) {
 			/* Special hack: user can direct multicasts
 			   and limited broadcast via necessary interface
diff -urN --exclude-from=kernel-exclude linux-2.4.4/net/ipv4/tcp_ipv4.c linux-2.4.4-udp/net/ipv4/tcp_ipv4.c
--- linux-2.4.4/net/ipv4/tcp_ipv4.c	Wed Apr 25 23:57:39 2001
+++ linux-2.4.4-udp/net/ipv4/tcp_ipv4.c	Sun Jul  1 17:02:10 2001
@@ -147,6 +147,30 @@
 
 	spin_lock(&head->lock);
 	tb = (struct tcp_bind_bucket *)sk->prev;
+
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+        if (child->num != sk->num) {
+        	struct tcp_bind_hashbucket *head_new;
+                unsigned short snum = child->num;
+                
+                head_new = &tcp_bhash[tcp_bhashfn(snum)];
+                spin_lock(&head_new->lock);
+                
+                for(tb = head_new->chain; 
+                    tb && tb->port != snum;
+                    tb = tb->next) 
+   			;
+                if (tb == NULL) {
+ 			tb = (struct tcp_bind_bucket *)sk->prev;
+                	spin_unlock(&head_new->lock);
+ 		}
+ 		else {
+ 			spin_unlock(&head);
+ 			head = head_new;
+ 		}
+	}
+#endif
+
 	if ((child->bind_next = tb->owners) != NULL)
 		tb->owners->bind_pprev = &child->bind_next;
 	tb->owners = child;
@@ -162,6 +186,212 @@
 	local_bh_enable();
 }
 
+
+static struct open_request *tcp_v4_search_req(struct tcp_opt *tp, 
+					      struct iphdr *iph,
+					      struct tcphdr *th,
+					      struct open_request ***prevp);
+					      
+__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif);
+
+
+#ifdef CONFIG_IP_INTERCEPT
+
+/*
+   Seems, I never wrote nothing more stupid.
+   I hope Gods will forgive me, but I cannot forgive myself 8)
+                                                --ANK (981001)
+ */
+
+static struct sock *tcp_v4_search_proxy_openreq(struct sk_buff *skb)
+{
+        struct iphdr *iph = skb->nh.iph;
+        struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4);
+        struct sock *sk;
+        int i;
+
+        for (i=0; i<TCP_LHTABLE_SIZE; i++) {
+                for(sk = tcp_listening_hash[i]; sk; sk = sk->next) {
+                        struct open_request **dummy;
+                        if (tcp_v4_search_req(&sk->tp_pinfo.af_tcp, iph,
+                                              th, &dummy) &&
+                            (!sk->bound_dev_if ||
+                             sk->bound_dev_if == skb->dev->ifindex))
+                                return sk;
+                }
+        }
+        return NULL;
+}
+
+/*
+ *      Check whether a received TCP packet might be for one of our
+ *      connections.
+ */
+
+int tcp_chksock(struct sk_buff *skb)
+{
+        struct iphdr *iph = skb->nh.iph;
+        struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4);
+        struct sock *sk;
+
+        if (ntohs(iph->tot_len) - iph->ihl*4 < sizeof(struct tcphdr))
+                return 0;
+        
+        printk(KERN_DEBUG "tcp_chksock 1");
+        sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr,
+                           th->dest, skb->dev->ifindex);
+
+        if (!sk) {
+	        printk(KERN_DEBUG "tcp_chksock 2");
+                return tcp_v4_search_proxy_openreq(skb) != NULL;
+        }
+
+        if (sk->state == TCP_LISTEN) {
+                struct open_request **dummy;
+	        printk(KERN_DEBUG "tcp_chksock 3");
+
+                if (tcp_v4_search_req(&sk->tp_pinfo.af_tcp, skb->nh.iph,
+                                      th, &dummy) &&
+                    (!sk->bound_dev_if ||
+                     sk->bound_dev_if == skb->dev->ifindex)) {
+		        printk(KERN_DEBUG "tcp_chksock 4");
+                        return 1;
+                }
+        }
+
+        /* 0 means accept all LOCAL addresses here, not all the world... */
+
+        if (sk->rcv_saddr == 0)
+                return 0;
+                
+	printk(KERN_DEBUG "tcp_chksock 5");
+
+        return 1;
+}
+
+#endif
+
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+
+/* Ensure that the bound bucket for the port exists.
+ * Return 0 and bump bucket reference count on success.
+ *
+ */
+static __inline__ int __tcp_bucket_check(unsigned short snum)
+{
+	struct tcp_bind_hashbucket *head;
+        struct tcp_bind_bucket *tb;
+        int rc = 0;
+
+	head = &tcp_bhash[tcp_bhashfn(snum)];
+	
+	spin_lock(&head->lock);
+        for(tb = head->chain; (tb && (tb->port != snum)); tb = tb->next)
+                ;
+        if (tb == NULL) {
+                if ((tb = tcp_bucket_create(head, snum)) == NULL) {
+                        rc = 1;
+                }
+        }
+        spin_unlock(&head->lock);
+
+        return rc;
+}
+
+/* Cleaned up a little and adapted to new bind bucket scheme.
+ * Oddly, this should increase performance here for
+ * transparent proxy, as tests within the inner loop have
+ * been eliminated. -DaveM
+ */
+static struct sock *__tcp_v4_proxy_lookup(unsigned long raddr, unsigned short rnum, 
+					unsigned long laddr, unsigned short lnum, 
+                                        unsigned short pnum,
+                                        struct net_device *dev, 
+                                        int dif)
+{
+        struct sock *s, *result = NULL;
+        int badness = -1;
+        u32 paddr = 0;
+        unsigned short hnum = ntohs(lnum);
+        unsigned short hpnum = ntohs(pnum);
+        struct tcp_bind_hashbucket *head;
+        struct tcp_bind_bucket *tb;
+
+        if(dev && dev->ip_ptr) {
+                struct in_device *idev = dev->ip_ptr;
+
+                if(idev->ifa_list)
+                        paddr = idev->ifa_list->ifa_local;
+        }
+
+	printk("tcpv4proxylookup: hnum=%d, hpnum=%d\n", hnum, hpnum);
+
+	head = &tcp_bhash[tcp_bhashfn(hnum)];
+
+	spin_lock(&head->lock);
+	tb = head->chain;
+        for( ; (tb && tb->port != hnum); tb = tb->next)
+        	;
+        if (tb == NULL) {
+
+		printk("tbnull1\n");
+                spin_unlock(&head->lock);
+                
+		/* if we can't find an explicitly bound socket, try the redirected port */
+                head = &tcp_bhash[tcp_bhashfn(hpnum)];
+                spin_lock(&head->lock);
+                	
+        	for(tb = head->chain ; (tb && tb->port != hpnum); tb = tb->next)
+                	;
+	        if (!tb) {
+	        	goto unlock;
+	        }
+        }
+        s = tb->owners;
+	printk("tbnemnull1\n");
+        /* FIXME: should we lock struct sock *s somehow? */
+
+        for(; s; s = s->bind_next) {
+                int score = 0;
+                if(s->rcv_saddr) {
+                        if((s->num != hpnum || s->rcv_saddr != paddr) &&
+                           (s->num != hnum || s->rcv_saddr != laddr))
+                                continue;
+                        score++;
+                }
+                if(s->daddr) {
+                        if(s->daddr != raddr)
+                                continue;
+                        score++;
+                }
+                if(s->dport) {
+                        if(s->dport != rnum)
+                                continue;
+                        score++;
+                }
+                if(s->bound_dev_if) {
+                        if(s->bound_dev_if != dif)
+                                continue;
+                        score++;
+                }
+                if(score == 4 && s->num == hnum) {
+                        result = s;
+                        goto gotit;
+                } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
+                        result = s;
+                        badness = score;
+                }
+        }
+
+gotit:
+	sock_hold(result);
+unlock:
+	spin_unlock(&head->lock);        
+	return result;
+}
+
+#endif
+
 /* Obtain a reference to a local port for the given sock,
  * if snum is zero it means select any available local port.
  */
@@ -760,6 +990,9 @@
 	     (req = *prev) != NULL;
 	     prev = &req->dl_next) {
 		if (req->rmt_port == rport &&
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+                    req->af.v4_req.lcl_port == th->dest &&
+#endif
 		    req->af.v4_req.rmt_addr == raddr &&
 		    req->af.v4_req.loc_addr == iph->daddr &&
 		    TCP_INET_FAMILY(req->class->family)) {
@@ -1036,8 +1269,14 @@
 	if (th->rst)
 		return;
 
-	if (((struct rtable*)skb->dst)->rt_type != RTN_LOCAL)
+	if (((struct rtable*)skb->dst)->rt_type != RTN_LOCAL) {
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+                if (((struct rtable*)skb->dst)->rt_type == RTN_UNICAST)
+                        icmp_send(skb, ICMP_DEST_UNREACH,
+                                  ICMP_PORT_UNREACH, 0);
+#endif
 		return;
+	}
 
 	/* Swap the send and the receive. */
 	memset(&rth, 0, sizeof(struct tcphdr)); 
@@ -1160,6 +1399,7 @@
 	return &rt->u.dst;
 }
 
+
 /*
  *	Send a SYN-ACK after having received an ACK. 
  *	This still operates on a open_request only, not on a big
@@ -1181,6 +1421,9 @@
 	if (skb) {
 		struct tcphdr *th = skb->h.th;
 
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+		th->source = req->af.v4_req.lcl_port;
+#endif
 		th->check = tcp_v4_check(th, skb->len,
 					 req->af.v4_req.loc_addr, req->af.v4_req.rmt_addr,
 					 csum_partial((char *)th, skb->len, skb->csum));
@@ -1333,6 +1576,9 @@
 	req->af.v4_req.loc_addr = daddr;
 	req->af.v4_req.rmt_addr = saddr;
 	req->af.v4_req.opt = tcp_v4_save_options(sk, skb);
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+	req->af.v4_req.lcl_port = skb->h.th->dest;
+#endif
 	req->class = &or_ipv4;
 	if (!want_cookie)
 		TCP_ECN_create_request(req, skb->h.th);
@@ -1426,6 +1672,17 @@
 	    (dst = tcp_v4_route_req(sk, req)) == NULL)
 		goto exit;
 
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+        /* The new socket created for transparent proxy may fall
+         * into a non-existed bind bucket because sk->num != newsk->num.
+         * Ensure existance of the bucket now. The placement of the check
+         * later will require to destroy just created newsk in the case of fail.
+         * 1998/04/22 Andrey V. Savochkin <saw@msu.ru>
+         */
+        if (__tcp_bucket_check(ntohs(skb->h.th->dest)))
+                goto exit;
+#endif
+
 	newsk = tcp_create_openreq_child(sk, req, skb);
 	if (!newsk)
 		goto exit;
@@ -1441,6 +1698,10 @@
 	req->af.v4_req.opt = NULL;
 	newsk->protinfo.af_inet.mc_index = tcp_v4_iif(skb);
 	newsk->protinfo.af_inet.mc_ttl = skb->nh.iph->ttl;
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+	newsk->num = ntohs(skb->h.th->dest);
+	newsk->sport = req->af.v4_req.lcl_port;
+#endif
 	newtp->ext_header_len = 0;
 	if (newsk->protinfo.af_inet.opt)
 		newtp->ext_header_len = newsk->protinfo.af_inet.opt->optlen;
@@ -1628,8 +1889,22 @@
 	TCP_SKB_CB(skb)->flags = skb->nh.iph->tos;
 	TCP_SKB_CB(skb)->sacked = 0;
 
+#if CONFIG_IP_STATELESS_REDIRECT
+	if (IPCB(skb)->redirport) {
+		sk = __tcp_v4_proxy_lookup(skb->nh.iph->saddr, th->source,
+                                           skb->nh.iph->daddr, th->dest,
+                                           IPCB(skb)->redirport,
+                                           skb->dev, tcp_v4_iif(skb));
+	} else {
+		sk = __tcp_v4_lookup(skb->nh.iph->saddr, th->source,
+				     skb->nh.iph->daddr, ntohs(th->dest), tcp_v4_iif(skb));
+		if (!sk)
+			sk = tcp_v4_search_proxy_openreq(skb);
+	}
+#else
 	sk = __tcp_v4_lookup(skb->nh.iph->saddr, th->source,
 			     skb->nh.iph->daddr, ntohs(th->dest), tcp_v4_iif(skb));
+#endif
 
 	if (!sk)
 		goto no_tcp_socket;
diff -urN --exclude-from=kernel-exclude linux-2.4.4/net/ipv4/udp.c linux-2.4.4-udp/net/ipv4/udp.c
--- linux-2.4.4/net/ipv4/udp.c	Thu Apr 12 21:11:39 2001
+++ linux-2.4.4-udp/net/ipv4/udp.c	Sun Jul  1 17:03:43 2001
@@ -94,6 +94,8 @@
 #include <net/inet_common.h>
 #include <net/checksum.h>
 
+#define IP_CMSG_TPROXY	32
+
 /*
  *	Snmp MIB for the UDP layer
  */
@@ -205,6 +207,89 @@
 	write_unlock_bh(&udp_hash_lock);
 }
 
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+#define secondlist(hpnum, sk, fpass) \
+({ struct sock *s1; if(!(sk) && (fpass)--) \
+        s1 = udp_hash[(hpnum) & (UDP_HTABLE_SIZE - 1)]; \
+   else \
+        s1 = (sk); \
+   s1; \
+})
+
+#define udp_v4_proxy_loop_init(hnum, hpnum, sk, fpass) \
+        secondlist((hpnum), udp_hash[(hnum)&(UDP_HTABLE_SIZE-1)],(fpass))
+
+#define udp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \
+        secondlist((hpnum),(sk)->next,(fpass))
+
+static struct sock *udp_v4_proxy_lookup(unsigned long raddr, unsigned short rnum, 
+					unsigned long laddr, unsigned short lnum,
+					unsigned short pnum,
+                                        struct net_device *dev)
+{
+        struct sock *s, *result = NULL;
+        int badness = -1;
+        u32 paddr = 0;
+        unsigned short hnum = ntohs(lnum);  
+        unsigned short hpnum = ntohs(pnum);
+        int firstpass = 1;
+        int dif = dev->ifindex;
+
+        if(dev && dev->ip_ptr) {
+                struct in_device *idev = dev->ip_ptr;
+
+                if(idev->ifa_list)
+                        paddr = idev->ifa_list->ifa_local;
+        }
+	read_lock(&udp_hash_lock);
+ 	printk("proxylookup hnum=%d, hpnum=%d\n", hnum, hpnum);
+        for(s = udp_v4_proxy_loop_init(hnum, hpnum, s, firstpass);
+            s != NULL;
+ 		s = udp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) {
+ 		printk("s->num=%d, hnum=%d, hpnum=%d\n", s->num, hnum, hpnum);
+                if(s->num == hnum || s->num == hpnum) {
+                        int score = 0;
+                      	if(s->dead && (s->state == TCP_CLOSE))
+                                continue; 
+                        if(s->rcv_saddr) {
+                                if((s->num != hpnum || s->rcv_saddr != paddr) &&
+			 	   (s->num != hnum || s->rcv_saddr != laddr))
+                         	        continue;
+                         	score++;
+			}
+                        if(s->daddr) {
+                                if(s->daddr != raddr)
+         		 	        continue;
+			 	score++;
+                        }
+                        if(s->dport) {
+                                if(s->dport != rnum)
+                         	        continue;
+                         	score++;
+                        }
+                        if(s->bound_dev_if) {
+                                if(s->bound_dev_if != dif)
+                         	        continue;
+                         	score++;
+                        }
+                        if(score == 4 && s->num == hnum) {
+                                result = s;
+                                break;
+                        } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
+                 	                result = s;
+         	 	 		badness = score;
+         	 	}
+         	}
+ 	}
+ 	if (result)
+ 		sock_hold(result);
+ 		
+	read_unlock(&udp_hash_lock);
+ 	return result;
+}
+#endif
+
+
 /* UDP is nearly always wildcards out the wazoo, it makes no sense to try
  * harder than this. -DaveM
  */
@@ -413,6 +498,57 @@
 				   fraglen);
 }
 
+int udp_cmsg_send(struct msghdr *msg, u32 *tos, struct udpfakehdr *ufh, struct ipcm_cookie *ipc)
+{
+	struct cmsghdr *cmsg;
+
+	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+		if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
+		    (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+				    + cmsg->cmsg_len) > msg->msg_controllen) {
+			return -EINVAL;
+		}
+		if (cmsg->cmsg_level != SOL_UDP)
+			continue;
+		switch (cmsg->cmsg_type) {
+		case UDP_TPROXY:
+		{
+			struct sockaddr_in *addr;
+			
+			if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
+			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sockaddr_in)))
+				return -EINVAL;
+			addr = (struct sockaddr_in *) CMSG_DATA(cmsg);
+			if (addr->sin_family != AF_INET)
+				return -EINVAL;
+			ipc->addr = addr->sin_addr.s_addr;
+			ufh->uh.source = addr->sin_port;
+			(*tos) |= RTO_TPROXY;
+			break;
+		}
+		default:
+			return -EINVAL;
+		}
+	}
+	return ip_cmsg_send(msg, ipc);
+}
+
+void udp_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
+{
+	unsigned flags = skb->sk->protinfo.af_inet.cmsg_flags;
+
+	if (flags & IP_CMSG_TPROXY) {
+		struct sockaddr_in addr;
+		
+		addr.sin_family = AF_INET;
+		addr.sin_port = skb->h.uh->dest;
+		addr.sin_addr.s_addr = skb->nh.iph->daddr;
+		put_cmsg(msg, SOL_UDP, UDP_TPROXY, sizeof(addr), &addr);
+	}
+	ip_cmsg_recv(msg, skb);
+}
+
 int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
 	int ulen = len + sizeof(struct udphdr);
@@ -422,7 +558,7 @@
 	int free = 0;
 	int connected = 0;
 	u32 daddr;
-	u8  tos;
+	u32 tos;
 	int err;
 
 	/* This check is ONLY to check for arithmetic overflow
@@ -476,10 +612,12 @@
 	ipc.addr = sk->saddr;
 	ufh.uh.source = sk->sport;
 
+	tos = RT_TOS(sk->protinfo.af_inet.tos);
+
 	ipc.opt = NULL;
 	ipc.oif = sk->bound_dev_if;
 	if (msg->msg_controllen) {
-		err = ip_cmsg_send(msg, &ipc);
+		err = udp_cmsg_send(msg, &tos, &ufh, &ipc);
 		if (err)
 			return err;
 		if (ipc.opt)
@@ -498,7 +636,6 @@
 		daddr = ipc.opt->faddr;
 		connected = 0;
 	}
-	tos = RT_TOS(sk->protinfo.af_inet.tos);
 	if (sk->localroute || (msg->msg_flags&MSG_DONTROUTE) || 
 	    (ipc.opt && ipc.opt->is_strictroute)) {
 		tos |= RTO_ONLINK;
@@ -679,7 +816,7 @@
 		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
   	}
 	if (sk->protinfo.af_inet.cmsg_flags)
-		ip_cmsg_recv(msg, skb);
+		udp_cmsg_recv(msg, skb);
 	err = copied;
   
 out_free:
@@ -724,6 +861,7 @@
 	sk_dst_reset(sk);
 
 	err = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr,
+			       ((sk->protinfo.af_inet.cmsg_flags & IP_CMSG_TPROXY) ? RTO_TPROXY : 0) | 
 			       sk->protinfo.af_inet.tos|sk->localroute, sk->bound_dev_if);
 	if (err)
 		return err;
@@ -867,6 +1005,33 @@
 	return 0;
 }
 
+#ifdef CONFIG_IP_INTERCEPT
+/*
+ *      Check whether a received UDP packet might be for one of our
+ *      sockets.
+ */
+
+int udp_chksock(struct sk_buff *skb)
+{
+        struct iphdr *iph = skb->nh.iph;
+        struct udphdr *uh = (struct udphdr *)(skb->nh.raw + iph->ihl*4);
+        struct sock *sk;
+
+        if (ntohs(iph->tot_len) - iph->ihl*4 < sizeof(struct udphdr))
+                return 0;
+        sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest, skb->dev->ifindex);
+        if (!sk)
+                return 0;
+
+        /* 0 means accept all LOCAL addresses here, not all the world... */
+        if (sk->rcv_saddr == 0)
+                return 0;
+
+        return 1;
+}
+#endif
+
+
 /*
  *	All we need to do is get the socket, and then do a checksum. 
  */
@@ -902,7 +1067,16 @@
 	if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
 		return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
 
-	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex);
+	printk("skb %p, dev %p, uh %p\n", skb, skb->dev, uh);
+#ifdef CONFIG_IP_STATELESS_REDIRECT
+	if (IPCB(skb)->redirport)
+      	        sk = udp_v4_proxy_lookup(saddr, uh->source,
+  	                                 daddr, uh->dest,
+  	                                 IPCB(skb)->redirport,
+  	                                 skb->dev);
+	else
+#endif
+		sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex);
 
 	if (sk != NULL) {
 		udp_queue_rcv_skb(sk, skb);
@@ -1005,14 +1179,76 @@
 	return len;
 }
 
+int udp_setsockopt(struct sock *sk, int level, int optname, char *optval, 
+                   int optlen)
+{
+	int error = 0;
+	
+	if (level != SOL_UDP)
+		return ip_setsockopt(sk, level, optname, optval, optlen);
+	
+	lock_sock(sk);
+	if (optname == UDP_TPROXY) {
+		int val;
+		
+		if (optlen != sizeof(int)) {
+			error = -EINVAL;
+			goto err_unlock;
+		}
+		if (get_user(val, (int *) optval)) {
+			error = -EFAULT;
+			goto err_unlock;
+		}
+		if (val)
+			sk->protinfo.af_inet.cmsg_flags |= IP_CMSG_TPROXY;
+		else
+			sk->protinfo.af_inet.cmsg_flags &= ~IP_CMSG_TPROXY;
+	}
+err_unlock:
+	release_sock(sk);
+	return error;
+}
+
+int udp_getsockopt(struct sock *sk, int level, int optname, char *optval,
+                   int *optlen)
+{
+	int error = 0;
+	
+	if (level != SOL_UDP)
+		return ip_getsockopt(sk, level, optname, optval, optlen);
+		
+	lock_sock(sk);
+	if (optname == UDP_TPROXY) {
+		int val, len;
+		
+		if (get_user(len, optlen)) {
+			error = -EFAULT;
+			goto err_unlock;
+		}
+		if (len != sizeof(int)) {
+			error = -EINVAL;
+			goto err_unlock;
+		}
+		if (sk->protinfo.af_inet.cmsg_flags & IP_CMSG_TPROXY)
+			val = 1;
+		else
+			val = 0;
+		if (put_user(val, optval))
+			error = -EINVAL;
+	}
+err_unlock:
+	release_sock(sk);
+	return error;
+}
+
 struct proto udp_prot = {
  	name:		"UDP",
 	close:		udp_close,
 	connect:	udp_connect,
 	disconnect:	udp_disconnect,
 	ioctl:		udp_ioctl,
-	setsockopt:	ip_setsockopt,
-	getsockopt:	ip_getsockopt,
+	setsockopt:	udp_setsockopt,
+	getsockopt:	udp_getsockopt,
 	sendmsg:	udp_sendmsg,
 	recvmsg:	udp_recvmsg,
 	backlog_rcv:	udp_queue_rcv_skb,
diff -urN --exclude-from=kernel-exclude linux-2.4.4/net/netsyms.c linux-2.4.4-udp/net/netsyms.c
--- linux-2.4.4/net/netsyms.c	Fri Apr 27 23:15:01 2001
+++ linux-2.4.4-udp/net/netsyms.c	Sat Jun 30 21:36:51 2001
@@ -257,6 +257,7 @@
 EXPORT_SYMBOL(inetdev_by_index);
 EXPORT_SYMBOL(in_dev_finish_destroy);
 EXPORT_SYMBOL(ip_defrag);
+EXPORT_SYMBOL(ip_local_deliver_finish);
 
 /* Route manipulation */
 EXPORT_SYMBOL(ip_rt_ioctl);

--UlVJffcvxoiEqYs2
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="iptables-1.1.2-TPROXY.diff"

diff -urN --exclude *.d iptables-1.1.2/extensions/Makefile iptables-1.1.2-TPROXY/extensions/Makefile
--- iptables-1.1.2/extensions/Makefile	Sun Aug 27 07:44:36 2000
+++ iptables-1.1.2-TPROXY/extensions/Makefile	Sat Jun 23 17:16:11 2001
@@ -1,6 +1,6 @@
 #! /usr/bin/make
 
-PF_EXT_SLIB:=tcp udp icmp mac limit standard REJECT LOG unclean state multiport tos TOS mark MARK owner SNAT DNAT MASQUERADE REDIRECT MIRROR
+PF_EXT_SLIB:=tcp udp icmp mac limit standard REJECT LOG unclean state multiport tos TOS TPROXY mark MARK owner SNAT DNAT MASQUERADE REDIRECT MIRROR
 PF6_EXT_SLIB:=tcp udp icmp standard 
 
 # Optionals
diff -urN --exclude *.d iptables-1.1.2/extensions/libipt_TPROXY.c iptables-1.1.2-TPROXY/extensions/libipt_TPROXY.c
--- iptables-1.1.2/extensions/libipt_TPROXY.c	Thu Jan  1 01:00:00 1970
+++ iptables-1.1.2-TPROXY/extensions/libipt_TPROXY.c	Sun Jul  1 16:24:50 2001
@@ -0,0 +1,118 @@
+/* Shared library add-on to iptables to add TPROXY target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_TPROXY.h>
+
+struct tosinfo {
+	struct ipt_entry_target t;
+	struct ipt_tproxy_target_info tproxy;
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+	printf(
+"TPROXY target v%s options:\n"
+"  --on-port port                   Redirect connection to port, or the original port if 0\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+	{ "on-port", 1, 0, '1' },
+	{ 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+static void
+parse_tproxy(const unsigned char *s, struct ipt_tproxy_target_info *info)
+{
+	int redir_port = string_to_number(s, 0, 65535);
+
+        info->redir_port = htons(redir_port);
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+	struct ipt_tproxy_target_info *tproxyinfo
+		= (struct ipt_tproxy_target_info *)(*target)->data;
+
+	switch (c) {
+	case '1':
+		if (*flags)
+			exit_error(PARAMETER_PROBLEM,
+			           "TPROXY target: Can't specify --to-port twice");
+		parse_tproxy(optarg, tproxyinfo);
+		*flags = 1;
+		break;
+
+	default:
+		return 0;
+	}
+
+	return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+	if (!flags)
+		exit_error(PARAMETER_PROBLEM,
+		           "TPROXY target: Parameter --to-port is required");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+	const struct ipt_tproxy_target_info *tproxyinfo =
+		(const struct ipt_tproxy_target_info *)target->data;
+	printf("TPROXY redirect %d", ntohs(tproxyinfo->redir_port));
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+	const struct ipt_tproxy_target_info *tproxyinfo =
+		(const struct ipt_tproxy_target_info *)target->data;
+
+	printf("--on-port %d ", ntohs(tproxyinfo->redir_port));
+}
+
+struct iptables_target tproxy
+= { NULL,
+    "TPROXY",
+    NETFILTER_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_tproxy_target_info)),
+    IPT_ALIGN(sizeof(struct ipt_tproxy_target_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+	register_target(&tproxy);
+}

--UlVJffcvxoiEqYs2--