[PATCH]: latest netfilter+ipsec patches

Patrick McHardy kaber@trash.net
Thu, 04 Mar 2004 23:30:38 +0100


This is a multi-part message in MIME format.
--------------060704090701060106030300
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

(long CC list, if anyone doesn't want to be on it anymore please say so)

This is the latest version of the netfilter+ipsec patches, I will check
them in in a couple of days after some minor changes.

(Noteworthy) changes since last version:

- hook-traversal on input is symetrical to output now. This means
packets will traverse PRE_ROUTING/LOCAL_IN before decryption and
PRE_REROUTING/LOCAL_IN or PRE_ROUTING/FORWARD afterwards.

The encrypted packets go through the stack, including the hooks,
the usual way. Packets reposted into the stack (tunnel-mode SAs)
skip the hooks until we know decryption is finished. If after
input-routing one of these packets has a non-local destination,
we know decyption is finished. The function nf_postxfrm_nonlocal()
which passes them to PRE_ROUTING is called for these packets.
Packets with local-destination are handled in
ip_local_deliver_finish(), if the ip-protocol handler is not marked
as xfrm_prot (all ipsec-transformers), decryption is finished and
packets go to nf_postxfrm_input() where they will traverse the
PRE_ROUTING hook, potentially be rerouted or traverse the LOCAL_IN
hook, before beeing delivered to the ip-protocol handler. Packets
from transport-mode SAs are handled exactly like local-packets
from tunnel-mode SAs.

The only problem with this approach I'm currently aware of is that
one of the ip-protocol handlers marked with xfrm_prot
(xfrm4_tunnel.c) is also responsible for dispatching packets to
ipip.c; the encapsulated packets heading for a ipip-tunnel won't
traverse the netfilter hooks on input.

- new match "policy" for matching the policy that was used during
decapsulation (well, the used SAs, policy checks come later), and the
policy that will be used for encapsulation.

I had some examples but I accidentally killed X before sending, for
now I have just attached the test-script I used, if there are
questions just ask.


Regards
Patrick

--------------060704090701060106030300
Content-Type: text/x-patch;
 name="01-nf_reset.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="01-nf_reset.diff"

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/02/22 14:27:46+01:00 kaber@trash.net 
#   Add new function 'nf_reset' to reset netfilter related skb-fields
# 
# net/ipv6/sit.c
#   2004/02/22 14:27:39+01:00 kaber@trash.net +2 -14
#   Add new function 'nf_reset' to reset netfilter related skb-fields
# 
# net/ipv6/ip6_tunnel.c
#   2004/02/22 14:27:39+01:00 kaber@trash.net +1 -7
#   Add new function 'nf_reset' to reset netfilter related skb-fields
# 
# net/ipv4/ipip.c
#   2004/02/22 14:27:39+01:00 kaber@trash.net +2 -14
#   Add new function 'nf_reset' to reset netfilter related skb-fields
# 
# net/ipv4/ip_input.c
#   2004/02/22 14:27:39+01:00 kaber@trash.net +1 -5
#   Add new function 'nf_reset' to reset netfilter related skb-fields
# 
# net/ipv4/ip_gre.c
#   2004/02/22 14:27:39+01:00 kaber@trash.net +2 -14
#   Add new function 'nf_reset' to reset netfilter related skb-fields
# 
# include/linux/skbuff.h
#   2004/02/22 14:27:39+01:00 kaber@trash.net +12 -3
#   Add new function 'nf_reset' to reset netfilter related skb-fields
# 
diff -Nru a/include/linux/skbuff.h b/include/linux/skbuff.h
--- a/include/linux/skbuff.h	Sun Feb 22 14:35:04 2004
+++ b/include/linux/skbuff.h	Sun Feb 22 14:35:04 2004
@@ -1204,6 +1204,14 @@
 	if (nfct)
 		atomic_inc(&nfct->master->use);
 }
+static inline void nf_reset(struct sk_buff *skb)
+{
+	nf_conntrack_put(skb->nfct);
+	skb->nfct = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug = 0;
+#endif
+}
 
 #ifdef CONFIG_BRIDGE_NETFILTER
 static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
@@ -1216,9 +1224,10 @@
 	if (nf_bridge)
 		atomic_inc(&nf_bridge->use);
 }
-#endif
-
-#endif
+#endif /* CONFIG_BRIDGE_NETFILTER */
+#else /* CONFIG_NETFILTER */
+static inline void nf_reset(struct sk_buff *skb) {}
+#endif /* CONFIG_NETFILTER */
 
 #endif	/* __KERNEL__ */
 #endif	/* _LINUX_SKBUFF_H */
diff -Nru a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
--- a/net/ipv4/ip_gre.c	Sun Feb 22 14:35:04 2004
+++ b/net/ipv4/ip_gre.c	Sun Feb 22 14:35:04 2004
@@ -643,13 +643,7 @@
 		skb->dev = tunnel->dev;
 		dst_release(skb->dst);
 		skb->dst = NULL;
-#ifdef CONFIG_NETFILTER
-		nf_conntrack_put(skb->nfct);
-		skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-		skb->nf_debug = 0;
-#endif
-#endif
+		nf_reset(skb);
 		ipgre_ecn_decapsulate(iph, skb);
 		netif_rx(skb);
 		read_unlock(&ipgre_lock);
@@ -877,13 +871,7 @@
 		}
 	}
 
-#ifdef CONFIG_NETFILTER
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-	skb->nf_debug = 0;
-#endif
-#endif
+	nf_reset(skb);
 
 	IPTUNNEL_XMIT();
 	tunnel->recursion--;
diff -Nru a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
--- a/net/ipv4/ip_input.c	Sun Feb 22 14:35:04 2004
+++ b/net/ipv4/ip_input.c	Sun Feb 22 14:35:04 2004
@@ -202,17 +202,13 @@
 
 #ifdef CONFIG_NETFILTER_DEBUG
 	nf_debug_ip_local_deliver(skb);
-	skb->nf_debug = 0;
 #endif /*CONFIG_NETFILTER_DEBUG*/
 
 	__skb_pull(skb, ihl);
 
-#ifdef CONFIG_NETFILTER
 	/* Free reference early: we don't need it any more, and it may
            hold ip_conntrack module loaded indefinitely. */
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#endif /*CONFIG_NETFILTER*/
+	nf_reset(skb);
 
         /* Point into the IP datagram, just past the header. */
         skb->h.raw = skb->data;
diff -Nru a/net/ipv4/ipip.c b/net/ipv4/ipip.c
--- a/net/ipv4/ipip.c	Sun Feb 22 14:35:04 2004
+++ b/net/ipv4/ipip.c	Sun Feb 22 14:35:04 2004
@@ -496,13 +496,7 @@
 		skb->dev = tunnel->dev;
 		dst_release(skb->dst);
 		skb->dst = NULL;
-#ifdef CONFIG_NETFILTER
-		nf_conntrack_put(skb->nfct);
-		skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-		skb->nf_debug = 0;
-#endif
-#endif
+		nf_reset(skb);
 		ipip_ecn_decapsulate(iph, skb);
 		netif_rx(skb);
 		read_unlock(&ipip_lock);
@@ -647,13 +641,7 @@
 	if ((iph->ttl = tiph->ttl) == 0)
 		iph->ttl	=	old_iph->ttl;
 
-#ifdef CONFIG_NETFILTER
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-	skb->nf_debug = 0;
-#endif
-#endif
+	nf_reset(skb);
 
 	IPTUNNEL_XMIT();
 	tunnel->recursion--;
diff -Nru a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
--- a/net/ipv6/ip6_tunnel.c	Sun Feb 22 14:35:04 2004
+++ b/net/ipv6/ip6_tunnel.c	Sun Feb 22 14:35:04 2004
@@ -715,13 +715,7 @@
 	ipv6h->nexthdr = proto;
 	ipv6_addr_copy(&ipv6h->saddr, &fl.fl6_src);
 	ipv6_addr_copy(&ipv6h->daddr, &fl.fl6_dst);
-#ifdef CONFIG_NETFILTER
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-	skb->nf_debug = 0;
-#endif
-#endif
+	nf_reset(skb);
 	pkt_len = skb->len;
 	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, 
 		      skb->dst->dev, dst_output);
diff -Nru a/net/ipv6/sit.c b/net/ipv6/sit.c
--- a/net/ipv6/sit.c	Sun Feb 22 14:35:04 2004
+++ b/net/ipv6/sit.c	Sun Feb 22 14:35:04 2004
@@ -388,13 +388,7 @@
 		skb->dev = tunnel->dev;
 		dst_release(skb->dst);
 		skb->dst = NULL;
-#ifdef CONFIG_NETFILTER
-		nf_conntrack_put(skb->nfct);
-		skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-		skb->nf_debug = 0;
-#endif
-#endif
+		nf_reset(skb);
 		ipip6_ecn_decapsulate(iph, skb);
 		netif_rx(skb);
 		read_unlock(&ipip6_lock);
@@ -580,13 +574,7 @@
 	if ((iph->ttl = tiph->ttl) == 0)
 		iph->ttl	=	iph6->hop_limit;
 
-#ifdef CONFIG_NETFILTER
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-	skb->nf_debug = 0;
-#endif
-#endif
+	nf_reset(skb);
 
 	IPTUNNEL_XMIT();
 	tunnel->recursion--;

--------------060704090701060106030300
Content-Type: text/x-patch;
 name="02-output_hooks.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="02-output_hooks.diff"

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/02/22 14:29:05+01:00 kaber@trash.net 
#   Pass packets to POST_ROUTING hook before encryption and LOCAL_OUT afterwards
# 
# net/ipv4/xfrm4_tunnel.c
#   2004/02/22 14:28:58+01:00 kaber@trash.net +1 -0
#   Pass packets to POST_ROUTING hook before encryption and LOCAL_OUT afterwards
# 
# net/ipv4/ipcomp.c
#   2004/02/22 14:28:58+01:00 kaber@trash.net +1 -0
#   Pass packets to POST_ROUTING hook before encryption and LOCAL_OUT afterwards
# 
# net/ipv4/ip_output.c
#   2004/02/22 14:28:58+01:00 kaber@trash.net +20 -4
#   Pass packets to POST_ROUTING hook before encryption and LOCAL_OUT afterwards
# 
# net/ipv4/ip_forward.c
#   2004/02/22 14:28:58+01:00 kaber@trash.net +2 -1
#   Pass packets to POST_ROUTING hook before encryption and LOCAL_OUT afterwards
# 
# net/ipv4/esp4.c
#   2004/02/22 14:28:58+01:00 kaber@trash.net +1 -0
#   Pass packets to POST_ROUTING hook before encryption and LOCAL_OUT afterwards
# 
# net/ipv4/ah4.c
#   2004/02/22 14:28:58+01:00 kaber@trash.net +1 -0
#   Pass packets to POST_ROUTING hook before encryption and LOCAL_OUT afterwards
# 
# include/net/ip.h
#   2004/02/22 14:28:58+01:00 kaber@trash.net +1 -0
#   Pass packets to POST_ROUTING hook before encryption and LOCAL_OUT afterwards
# 
# include/linux/netfilter.h
#   2004/02/22 14:28:58+01:00 kaber@trash.net +9 -4
#   Pass packets to POST_ROUTING hook before encryption and LOCAL_OUT afterwards
# 
diff -Nru a/include/linux/netfilter.h b/include/linux/netfilter.h
--- a/include/linux/netfilter.h	Sun Feb 22 14:35:13 2004
+++ b/include/linux/netfilter.h	Sun Feb 22 14:35:13 2004
@@ -119,12 +119,14 @@
 /* This is gross, but inline doesn't cut it for avoiding the function
    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
 #ifdef CONFIG_NETFILTER_DEBUG
-#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
- nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
+#define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond)		\
+(!(cond)									\
+ ? (okfn)(skb) 								\
+ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
 #define NF_HOOK_THRESH nf_hook_slow
 #else
-#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
-(list_empty(&nf_hooks[(pf)][(hook)])					\
+#define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond)		\
+(!(cond) || list_empty(&nf_hooks[(pf)][(hook)])				\
  ? (okfn)(skb)								\
  : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
 #define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
@@ -132,6 +134,8 @@
  ? (okfn)(skb)								\
  : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
 #endif
+#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ NF_HOOK_COND((pf), (hook), (skb), (indev), (outdev), (okfn), 1)
 
 int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
 		 struct net_device *indev, struct net_device *outdev,
@@ -164,6 +168,7 @@
 
 #else /* !CONFIG_NETFILTER */
 #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
+#define NF_HOOK_COND NF_HOOK
 #endif /*CONFIG_NETFILTER*/
 
 #endif /*__KERNEL__*/
diff -Nru a/include/net/ip.h b/include/net/ip.h
--- a/include/net/ip.h	Sun Feb 22 14:35:13 2004
+++ b/include/net/ip.h	Sun Feb 22 14:35:13 2004
@@ -48,6 +48,7 @@
 #define IPSKB_TRANSLATED	2
 #define IPSKB_FORWARDED		4
 #define IPSKB_XFRM_TUNNEL_SIZE	8
+#define IPSKB_XFRM_TRANSFORMED	16
 };
 
 struct ipcm_cookie
diff -Nru a/net/ipv4/ah4.c b/net/ipv4/ah4.c
--- a/net/ipv4/ah4.c	Sun Feb 22 14:35:13 2004
+++ b/net/ipv4/ah4.c	Sun Feb 22 14:35:13 2004
@@ -145,6 +145,7 @@
 		err = -EHOSTUNREACH;
 		goto error_nolock;
 	}
+	IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
 	return NET_XMIT_BYPASS;
 
 error:
diff -Nru a/net/ipv4/esp4.c b/net/ipv4/esp4.c
--- a/net/ipv4/esp4.c	Sun Feb 22 14:35:13 2004
+++ b/net/ipv4/esp4.c	Sun Feb 22 14:35:13 2004
@@ -199,6 +199,7 @@
 		err = -EHOSTUNREACH;
 		goto error_nolock;
 	}
+	IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
 	return NET_XMIT_BYPASS;
 
 error:
diff -Nru a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
--- a/net/ipv4/ip_forward.c	Sun Feb 22 14:35:13 2004
+++ b/net/ipv4/ip_forward.c	Sun Feb 22 14:35:13 2004
@@ -51,7 +51,8 @@
 	if (unlikely(opt->optlen))
 		ip_forward_options(skb);
 
-	return dst_output(skb);
+	return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
+	                    skb->dst->dev, dst_output, skb->dst->xfrm != NULL);
 }
 
 int ip_forward(struct sk_buff *skb)
diff -Nru a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
--- a/net/ipv4/ip_output.c	Sun Feb 22 14:35:13 2004
+++ b/net/ipv4/ip_output.c	Sun Feb 22 14:35:13 2004
@@ -122,6 +122,12 @@
 	return ttl;
 }
 
+static inline int ip_dst_output(struct sk_buff *skb)
+{
+	return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
+	                    skb->dst->dev, dst_output, skb->dst->xfrm != NULL);
+}
+
 /* 
  *		Add an ip header to a skbuff and send it out.
  *
@@ -164,7 +170,7 @@
 
 	/* Send it out. */
 	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
-		       dst_output);
+		       ip_dst_output);
 }
 
 static inline int ip_finish_output2(struct sk_buff *skb)
@@ -282,7 +288,7 @@
 		return ip_finish_output(skb);
 }
 
-int ip_output(struct sk_buff *skb)
+static inline int ip_output2(struct sk_buff *skb)
 {
 	IP_INC_STATS(IpOutRequests);
 
@@ -293,6 +299,16 @@
 		return ip_finish_output(skb);
 }
 
+int ip_output(struct sk_buff *skb)
+{
+	int transformed = IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED;
+
+	if (transformed)
+		nf_reset(skb);
+	return NF_HOOK_COND(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,
+	                    skb->dst->dev, ip_output2, transformed);
+}
+
 int ip_queue_xmit(struct sk_buff *skb, int ipfragok)
 {
 	struct sock *sk = skb->sk;
@@ -386,7 +402,7 @@
 	skb->priority = sk->sk_priority;
 
 	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
-		       dst_output);
+		       ip_dst_output);
 
 no_route:
 	IP_INC_STATS(IpOutNoRoutes);
@@ -1165,7 +1181,7 @@
 
 	/* Netfilter gets whole the not fragmented skb. */
 	err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, 
-		      skb->dst->dev, dst_output);
+		      skb->dst->dev, ip_dst_output);
 	if (err) {
 		if (err > 0)
 			err = inet->recverr ? net_xmit_errno(err) : 0;
diff -Nru a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
--- a/net/ipv4/ipcomp.c	Sun Feb 22 14:35:13 2004
+++ b/net/ipv4/ipcomp.c	Sun Feb 22 14:35:13 2004
@@ -231,6 +231,7 @@
 		err = -EHOSTUNREACH;
 		goto error_nolock;
 	}
+	IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
 	err = NET_XMIT_BYPASS;
 
 out_exit:
diff -Nru a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c
--- a/net/ipv4/xfrm4_tunnel.c	Sun Feb 22 14:35:13 2004
+++ b/net/ipv4/xfrm4_tunnel.c	Sun Feb 22 14:35:13 2004
@@ -76,6 +76,7 @@
 		err = -EHOSTUNREACH;
 		goto error_nolock;
 	}
+	IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
 	return NET_XMIT_BYPASS;
 
 error_nolock:

--------------060704090701060106030300
Content-Type: text/x-patch;
 name="03-input-hooks.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="03-input-hooks.diff"

===== include/linux/netfilter_ipv4.h 1.6 vs edited =====
--- 1.6/include/linux/netfilter_ipv4.h	Wed Jan  7 06:38:33 2004
+++ edited/include/linux/netfilter_ipv4.h	Sun Feb 22 19:25:19 2004
@@ -83,6 +83,21 @@
    Returns true or false. */
 extern int skb_ip_make_writable(struct sk_buff **pskb,
 				unsigned int writable_len);
+
+#ifdef CONFIG_XFRM
+extern int nf_postxfrm_input(struct sk_buff *skb);
+extern int nf_postxfrm_nonlocal(struct sk_buff *skb);
+#else /* CONFIG_XFRM */
+static inline int nf_postxfrm_input(struct sk_buff *skb)
+{
+	return 0;
+}
+
+static inline int nf_postxfrm_nonlocal(struct sk_buff *skb)
+{
+	return 0;
+}
+#endif /* CONFIG_XFRM */
 #endif /*__KERNEL__*/
 
 #endif /*__LINUX_IP_NETFILTER_H*/
===== include/net/protocol.h 1.10 vs edited =====
--- 1.10/include/net/protocol.h	Sat May 10 14:25:34 2003
+++ edited/include/net/protocol.h	Sun Feb 22 19:25:08 2004
@@ -39,6 +39,7 @@
 	int			(*handler)(struct sk_buff *skb);
 	void			(*err_handler)(struct sk_buff *skb, u32 info);
 	int			no_policy;
+	int			xfrm_prot;
 };
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
===== net/core/netfilter.c 1.26 vs edited =====
--- 1.26/net/core/netfilter.c	Sun Sep 28 18:34:18 2003
+++ edited/net/core/netfilter.c	Sun Feb 22 19:25:19 2004
@@ -11,6 +11,7 @@
  */
 #include <linux/config.h>
 #include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
 #include <net/protocol.h>
 #include <linux/init.h>
 #include <linux/skbuff.h>
@@ -25,6 +26,7 @@
 #include <linux/icmp.h>
 #include <net/sock.h>
 #include <net/route.h>
+#include <net/xfrm.h>
 #include <linux/ip.h>
 
 #define __KERNEL_SYSCALLS__
@@ -628,9 +630,6 @@
 	struct dst_entry *odst;
 	unsigned int hh_len;
 
-	/* some non-standard hacks like ipt_REJECT.c:send_reset() can cause
-	 * packets with foreign saddr to appear on the NF_IP_LOCAL_OUT hook.
-	 */
 	if (inet_addr_type(iph->saddr) == RTN_LOCAL) {
 		fl.nl_u.ip4_u.daddr = iph->daddr;
 		fl.nl_u.ip4_u.saddr = iph->saddr;
@@ -681,6 +680,54 @@
 
 	return 0;
 }
+
+#ifdef CONFIG_XFRM
+static inline int nf_postxfrm_done(struct sk_buff *skb)
+{
+	return 0;
+}
+
+static inline int nf_postxfrm_input2(struct sk_buff *skb)
+{
+	if (inet_addr_type(skb->nh.iph->daddr) != RTN_LOCAL) {
+		if (ip_route_me_harder(&skb) == 0)
+			dst_input(skb);
+		else
+			kfree_skb(skb);
+		return -1;
+	}
+
+	return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
+	               nf_postxfrm_done);
+}
+
+int nf_postxfrm_input(struct sk_buff *skb)
+{
+	int off = skb->data - skb->nh.raw;
+
+	__skb_push(skb, off);
+	/* Fix header len and checksum if last xfrm was transport mode */
+	if (!skb->sp->x[skb->sp->len - 1].xvec->props.mode) {
+		skb->nh.iph->tot_len = htons(skb->len);
+		ip_send_check(skb->nh.iph);
+	}
+
+	nf_reset(skb);
+	if (NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
+	            nf_postxfrm_input2) != 0)
+		return -1;
+
+	__skb_pull(skb, off);
+	return 0;
+}
+
+int nf_postxfrm_nonlocal(struct sk_buff *skb)
+{
+	nf_reset(skb);
+	return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
+	               nf_postxfrm_done);
+}
+#endif /* CONFIG_XFRM */
 
 int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len)
 {
===== net/ipv4/ah4.c 1.30 vs edited =====
--- 1.30/net/ipv4/ah4.c	Sun Feb 22 14:28:58 2004
+++ edited/net/ipv4/ah4.c	Sun Feb 22 19:25:08 2004
@@ -344,6 +344,7 @@
 	.handler	=	xfrm4_rcv,
 	.err_handler	=	ah4_err,
 	.no_policy	=	1,
+	.xfrm_prot	=	1,
 };
 
 static int __init ah4_init(void)
===== net/ipv4/esp4.c 1.36 vs edited =====
--- 1.36/net/ipv4/esp4.c	Sun Feb 22 14:28:58 2004
+++ edited/net/ipv4/esp4.c	Sun Feb 22 19:25:09 2004
@@ -575,6 +575,7 @@
 	.handler	=	xfrm4_rcv,
 	.err_handler	=	esp4_err,
 	.no_policy	=	1,
+	.xfrm_prot	=	1,
 };
 
 static int __init esp4_init(void)
===== net/ipv4/ip_input.c 1.21 vs edited =====
--- 1.21/net/ipv4/ip_input.c	Sun Feb 22 14:27:39 2004
+++ edited/net/ipv4/ip_input.c	Sun Feb 22 21:20:47 2004
@@ -224,6 +224,12 @@
 	resubmit:
 		hash = protocol & (MAX_INET_PROTOS - 1);
 		raw_sk = sk_head(&raw_v4_htable[hash]);
+		ipprot = inet_protos[hash];
+		smp_read_barrier_depends();
+
+		if (skb->sp && !ipprot->xfrm_prot)
+			if (nf_postxfrm_input(skb))
+				goto out;
 
 		/* If there maybe a raw socket we must check - if not we
 		 * don't care less
@@ -231,10 +237,9 @@
 		if (raw_sk)
 			raw_v4_input(skb, skb->nh.iph, hash);
 
-		if ((ipprot = inet_protos[hash]) != NULL) {
+		if (ipprot != NULL) {
 			int ret;
 
-			smp_read_barrier_depends();
 			if (!ipprot->no_policy &&
 			    !xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
 				kfree_skb(skb);
@@ -279,8 +284,8 @@
 			return 0;
 	}
 
-	return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
-		       ip_local_deliver_finish);
+	return NF_HOOK_COND(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
+	                    ip_local_deliver_finish, !skb->sp);
 }
 
 static inline int ip_rcv_finish(struct sk_buff *skb)
@@ -346,6 +351,10 @@
 		}
 	}
 
+	if (skb->sp && !(((struct rtable *)skb->dst)->rt_flags&RTCF_LOCAL))
+		if (nf_postxfrm_nonlocal(skb))
+			goto drop;
+
 	return dst_input(skb);
 
 inhdr_error:
@@ -418,8 +427,8 @@
 		}
 	}
 
-	return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
-		       ip_rcv_finish);
+	return NF_HOOK_COND(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
+	                    ip_rcv_finish, !skb->sp);
 
 inhdr_error:
 	IP_INC_STATS_BH(IpInHdrErrors);
===== net/ipv4/ipcomp.c 1.19 vs edited =====
--- 1.19/net/ipv4/ipcomp.c	Sun Feb 22 14:28:58 2004
+++ edited/net/ipv4/ipcomp.c	Sun Feb 22 19:25:09 2004
@@ -408,6 +408,7 @@
 	.handler	=	xfrm4_rcv,
 	.err_handler	=	ipcomp4_err,
 	.no_policy	=	1,
+	.xfrm_prot	=	1,
 };
 
 static int __init ipcomp4_init(void)
===== net/ipv4/xfrm4_tunnel.c 1.10 vs edited =====
--- 1.10/net/ipv4/xfrm4_tunnel.c	Sun Feb 22 14:28:58 2004
+++ edited/net/ipv4/xfrm4_tunnel.c	Sun Feb 22 19:25:10 2004
@@ -171,6 +171,7 @@
 	.handler	=	ipip_rcv,
 	.err_handler	=	ipip_err,
 	.no_policy	=	1,
+	.xfrm_prot	=	1,
 };
 
 static int __init ipip_init(void)

--------------060704090701060106030300
Content-Type: text/x-patch;
 name="04-nat-policy_lookup.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="04-nat-policy_lookup.diff"

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/02/22 14:31:33+01:00 kaber@trash.net 
#   Add policy lookups to ip_route_me_harder, make NAT reroute for any change
#   in route/policy key
# 
# net/ipv4/netfilter/ip_nat_standalone.c
#   2004/02/22 14:31:26+01:00 kaber@trash.net +72 -8
#   Add policy lookups to ip_route_me_harder, make NAT reroute for any change
#   in route/policy key
# 
# net/ipv4/netfilter/ip_conntrack_standalone.c
#   2004/02/22 14:31:26+01:00 kaber@trash.net +1 -0
#   Add policy lookups to ip_route_me_harder, make NAT reroute for any change
#   in route/policy key
# 
# net/core/netfilter.c
#   2004/02/22 14:31:26+01:00 kaber@trash.net +15 -0
#   Add policy lookups to ip_route_me_harder, make NAT reroute for any change
#   in route/policy key
# 
diff -Nru a/net/core/netfilter.c b/net/core/netfilter.c
--- a/net/core/netfilter.c	Sun Feb 22 14:35:30 2004
+++ b/net/core/netfilter.c	Sun Feb 22 14:35:30 2004
@@ -27,6 +27,7 @@
 #include <net/sock.h>
 #include <net/route.h>
 #include <net/xfrm.h>
+#include <net/ip.h>
 #include <linux/ip.h>
 
 #define __KERNEL_SYSCALLS__
@@ -663,6 +664,20 @@
 	
 	if ((*pskb)->dst->error)
 		return -1;
+
+#ifdef CONFIG_XFRM
+	if (!(IPCB(*pskb)->flags & IPSKB_XFRM_TRANSFORMED)) {
+		struct xfrm_policy_afinfo *afinfo;
+
+		afinfo = xfrm_policy_get_afinfo(AF_INET);
+		if (afinfo != NULL) {
+			afinfo->decode_session(*pskb, &fl);
+			xfrm_policy_put_afinfo(afinfo);
+			if (xfrm_lookup(&(*pskb)->dst, &fl, (*pskb)->sk, 0) != 0)
+				return -1;
+		}
+	}
+#endif
 
 	/* Change in oif may mean change in hh_len. */
 	hh_len = (*pskb)->dst->dev->hard_header_len;
diff -Nru a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c
--- a/net/ipv4/netfilter/ip_conntrack_standalone.c	Sun Feb 22 14:35:30 2004
+++ b/net/ipv4/netfilter/ip_conntrack_standalone.c	Sun Feb 22 14:35:30 2004
@@ -489,6 +489,7 @@
 EXPORT_SYMBOL(ip_conntrack_alter_reply);
 EXPORT_SYMBOL(ip_conntrack_destroyed);
 EXPORT_SYMBOL(ip_conntrack_get);
+EXPORT_SYMBOL(__ip_conntrack_confirm);
 EXPORT_SYMBOL(need_ip_conntrack);
 EXPORT_SYMBOL(ip_conntrack_helper_register);
 EXPORT_SYMBOL(ip_conntrack_helper_unregister);
diff -Nru a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c
--- a/net/ipv4/netfilter/ip_nat_standalone.c	Sun Feb 22 14:35:30 2004
+++ b/net/ipv4/netfilter/ip_nat_standalone.c	Sun Feb 22 14:35:30 2004
@@ -166,6 +166,45 @@
 	return do_bindings(ct, ctinfo, info, hooknum, pskb);
 }
 
+struct nat_route_key
+{
+	u_int32_t addr;
+#ifdef CONFIG_XFRM
+	u_int16_t port;
+#endif
+};
+
+static inline void
+nat_route_key_get(struct sk_buff *skb, struct nat_route_key *key, int which)
+{
+	struct iphdr *iph = skb->nh.iph;
+
+	key->addr = which ? iph->daddr : iph->saddr;
+#ifdef CONFIG_XFRM
+	key->port = 0;
+	if (iph->protocol == IPPROTO_TCP || iph->protocol == IPPROTO_UDP) {
+		u_int16_t *ports = (u_int16_t *)(skb->nh.raw + iph->ihl*4);
+		key->port = ports[which];
+	}
+#endif
+}
+
+static inline int
+nat_route_key_compare(struct sk_buff *skb, struct nat_route_key *key, int which)
+{
+	struct iphdr *iph = skb->nh.iph;
+
+	if (key->addr != (which ? iph->daddr : iph->saddr))
+		return 1;
+#ifdef CONFIG_XFRM
+	if (iph->protocol == IPPROTO_TCP || iph->protocol == IPPROTO_UDP) {
+		u_int16_t *ports = (u_int16_t *)(skb->nh.raw + iph->ihl*4);
+		if (key->port != ports[which])
+			return 1;
+	}
+#endif
+}
+
 static unsigned int
 ip_nat_out(unsigned int hooknum,
 	   struct sk_buff **pskb,
@@ -173,6 +212,9 @@
 	   const struct net_device *out,
 	   int (*okfn)(struct sk_buff *))
 {
+	struct nat_route_key key;
+	unsigned int ret;
+
 	/* root is playing with raw sockets. */
 	if ((*pskb)->len < sizeof(struct iphdr)
 	    || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
@@ -195,7 +237,29 @@
 			return NF_STOLEN;
 	}
 
-	return ip_nat_fn(hooknum, pskb, in, out, okfn);
+	nat_route_key_get(*pskb, &key, 0);
+	ret = ip_nat_fn(hooknum, pskb, in, out, okfn);
+
+	if (ret != NF_DROP && ret != NF_STOLEN
+	    && nat_route_key_compare(*pskb, &key, 0)) {
+		if (ip_route_me_harder(pskb) != 0)
+			ret = NF_DROP;
+#ifdef CONFIG_XFRM
+		/*
+		 * POST_ROUTING hook is called with fixed outfn, we need
+		 * to manually confirm the packet and direct it to the
+		 * transformers if a policy matches.
+		 */
+		else if ((*pskb)->dst->xfrm != NULL) {
+			ret = ip_conntrack_confirm(*pskb);
+			if (ret != NF_DROP) {
+				dst_output(*pskb);
+				ret = NF_STOLEN;
+			}
+		}
+#endif
+	}
+	return ret;
 }
 
 #ifdef CONFIG_IP_NF_NAT_LOCAL
@@ -206,7 +270,7 @@
 		const struct net_device *out,
 		int (*okfn)(struct sk_buff *))
 {
-	u_int32_t saddr, daddr;
+	struct nat_route_key key;
 	unsigned int ret;
 
 	/* root is playing with raw sockets. */
@@ -214,14 +278,14 @@
 	    || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
 		return NF_ACCEPT;
 
-	saddr = (*pskb)->nh.iph->saddr;
-	daddr = (*pskb)->nh.iph->daddr;
-
+	nat_route_key_get(*pskb, &key, 1);
 	ret = ip_nat_fn(hooknum, pskb, in, out, okfn);
+
 	if (ret != NF_DROP && ret != NF_STOLEN
-	    && ((*pskb)->nh.iph->saddr != saddr
-		|| (*pskb)->nh.iph->daddr != daddr))
-		return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP;
+	    && nat_route_key_compare(*pskb, &key, 1)) {
+		if (ip_route_me_harder(pskb) != 0)
+			ret = NF_DROP;
+	}
 	return ret;
 }
 #endif

--------------060704090701060106030300
Content-Type: text/x-patch;
 name="05-nat-policy_checks.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="05-nat-policy_checks.diff"

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/02/22 14:33:14+01:00 kaber@trash.net 
#   Make policy checks find correct policy after NAT
# 
# net/xfrm/xfrm_policy.c
#   2004/02/22 14:33:07+01:00 kaber@trash.net +2 -0
#   Make policy checks find correct policy after NAT
# 
# net/ipv4/udp.c
#   2004/02/22 14:33:07+01:00 kaber@trash.net +2 -0
#   Make policy checks find correct policy after NAT
# 
# net/ipv4/tcp_ipv4.c
#   2004/02/22 14:33:07+01:00 kaber@trash.net +1 -0
#   Make policy checks find correct policy after NAT
# 
# net/ipv4/raw.c
#   2004/02/22 14:33:07+01:00 kaber@trash.net +1 -0
#   Make policy checks find correct policy after NAT
# 
# net/ipv4/ip_input.c
#   2004/02/22 14:33:07+01:00 kaber@trash.net +6 -8
#   Make policy checks find correct policy after NAT
# 
# net/core/netfilter.c
#   2004/02/22 14:33:07+01:00 kaber@trash.net +43 -0
#   Make policy checks find correct policy after NAT
# 
# include/linux/netfilter.h
#   2004/02/22 14:33:07+01:00 kaber@trash.net +16 -0
#   Make policy checks find correct policy after NAT
# 
diff -Nru a/include/linux/netfilter.h b/include/linux/netfilter.h
--- a/include/linux/netfilter.h	Sun Feb 22 14:35:36 2004
+++ b/include/linux/netfilter.h	Sun Feb 22 14:35:36 2004
@@ -171,5 +171,21 @@
 #define NF_HOOK_COND NF_HOOK
 #endif /*CONFIG_NETFILTER*/
 
+#ifdef CONFIG_XFRM
+#ifdef CONFIG_IP_NF_NAT_NEEDED
+struct flowi;
+extern void nf_nat_decode_session4(struct sk_buff *skb, struct flowi *fl);
+
+static inline void
+nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, int family)
+{
+	if (family == AF_INET)
+		nf_nat_decode_session4(skb, fl);
+}
+#else /* CONFIG_IP_NF_NAT_NEEDED */
+#define nf_nat_decode_session(skb,fl,family)
+#endif /* CONFIG_IP_NF_NAT_NEEDED */
+#endif /* CONFIG_XFRM */
+
 #endif /*__KERNEL__*/
 #endif /*__LINUX_NETFILTER_H*/
diff -Nru a/net/core/netfilter.c b/net/core/netfilter.c
--- a/net/core/netfilter.c	Sun Feb 22 14:35:36 2004
+++ b/net/core/netfilter.c	Sun Feb 22 14:35:36 2004
@@ -747,6 +747,49 @@
 	return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
 	               nf_postxfrm_done);
 }
+
+#ifdef CONFIG_IP_NF_NAT_NEEDED
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_nat.h>
+
+void nf_nat_decode_session4(struct sk_buff *skb, struct flowi *fl)
+{
+	struct ip_conntrack *ct;
+	struct ip_conntrack_tuple *t;
+	struct ip_nat_info_manip *m;
+	unsigned int i;
+
+	if (skb->nfct == NULL)
+		return;
+	ct = (struct ip_conntrack *)skb->nfct->master;
+
+	for (i = 0; i < ct->nat.info.num_manips; i++) {
+		m = &ct->nat.info.manips[i];
+		t = &ct->tuplehash[m->direction].tuple;
+
+		switch (m->hooknum) {
+		case NF_IP_PRE_ROUTING:
+			if (m->maniptype != IP_NAT_MANIP_DST)
+				break;
+			fl->fl4_dst = t->dst.ip;
+			if (t->dst.protonum == IPPROTO_TCP ||
+			    t->dst.protonum == IPPROTO_UDP)
+				fl->fl_ip_dport = t->dst.u.tcp.port;
+			break;
+#ifdef CONFIG_IP_NF_NAT_LOCAL
+		case NF_IP_LOCAL_IN:
+			if (m->maniptype != IP_NAT_MANIP_SRC)
+				break;
+			fl->fl4_src = t->src.ip;
+			if (t->dst.protonum == IPPROTO_TCP ||
+			    t->dst.protonum == IPPROTO_UDP)
+				fl->fl_ip_sport = t->src.u.tcp.port;
+			break;
+#endif
+		}
+	}
+}
+#endif /* CONFIG_IP_NF_NAT_NEEDED */
 #endif /* CONFIG_XFRM */
 
 int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len)
diff -Nru a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
--- a/net/ipv4/ip_input.c	Sun Feb 22 14:35:36 2004
+++ b/net/ipv4/ip_input.c	Sun Feb 22 14:35:36 2004
@@ -206,10 +206,6 @@
 
 	__skb_pull(skb, ihl);
 
-	/* Free reference early: we don't need it any more, and it may
-           hold ip_conntrack module loaded indefinitely. */
-	nf_reset(skb);
-
         /* Point into the IP datagram, just past the header. */
         skb->h.raw = skb->data;
 
@@ -240,10 +236,12 @@
 		if (ipprot != NULL) {
 			int ret;
 
-			if (!ipprot->no_policy &&
-			    !xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
-				kfree_skb(skb);
-				goto out;
+			if (!ipprot->no_policy) {
+				if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+					kfree_skb(skb);
+					goto out;
+				}
+				nf_reset(skb);
 			}
 			ret = ipprot->handler(skb);
 			if (ret < 0) {
diff -Nru a/net/ipv4/raw.c b/net/ipv4/raw.c
--- a/net/ipv4/raw.c	Sun Feb 22 14:35:36 2004
+++ b/net/ipv4/raw.c	Sun Feb 22 14:35:36 2004
@@ -249,6 +249,7 @@
 		kfree_skb(skb);
 		return NET_RX_DROP;
 	}
+	nf_reset(skb);
 
 	skb_push(skb, skb->data - skb->nh.raw);
 
diff -Nru a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
--- a/net/ipv4/tcp_ipv4.c	Sun Feb 22 14:35:36 2004
+++ b/net/ipv4/tcp_ipv4.c	Sun Feb 22 14:35:36 2004
@@ -1785,6 +1785,7 @@
 
 	if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
 		goto discard_and_relse;
+	nf_reset(skb);
 
 	if (sk_filter(sk, skb, 0))
 		goto discard_and_relse;
diff -Nru a/net/ipv4/udp.c b/net/ipv4/udp.c
--- a/net/ipv4/udp.c	Sun Feb 22 14:35:36 2004
+++ b/net/ipv4/udp.c	Sun Feb 22 14:35:36 2004
@@ -1027,6 +1027,7 @@
 		kfree_skb(skb);
 		return -1;
 	}
+	nf_reset(skb);
 
 	if (up->encap_type) {
 		/*
@@ -1192,6 +1193,7 @@
 
 	if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
 		goto drop;
+	nf_reset(skb);
 
 	/* No socket. Drop packet silently, if checksum is wrong */
 	if (udp_checksum_complete(skb))
diff -Nru a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
--- a/net/xfrm/xfrm_policy.c	Sun Feb 22 14:35:36 2004
+++ b/net/xfrm/xfrm_policy.c	Sun Feb 22 14:35:36 2004
@@ -21,6 +21,7 @@
 #include <linux/workqueue.h>
 #include <linux/notifier.h>
 #include <linux/netdevice.h>
+#include <linux/netfilter.h>
 #include <net/xfrm.h>
 #include <net/ip.h>
 
@@ -908,6 +909,7 @@
 
 	if (_decode_session(skb, &fl, family) < 0)
 		return 0;
+	nf_nat_decode_session(skb, &fl, family);
 
 	/* First, check used SA against their selectors. */
 	if (skb->sp) {

--------------060704090701060106030300
Content-Type: text/x-patch;
 name="06-policy-match.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="06-policy-match.diff"

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/03/02 06:08:55+01:00 kaber@trash.net 
#   Add policy match
# 
# net/ipv4/netfilter/ipt_policy.c
#   2004/03/02 06:08:46+01:00 kaber@trash.net +176 -0
# 
# include/linux/netfilter_ipv4/ipt_policy.h
#   2004/03/02 06:08:46+01:00 kaber@trash.net +52 -0
# 
# net/ipv4/netfilter/ipt_policy.c
#   2004/03/02 06:08:46+01:00 kaber@trash.net +0 -0
#   BitKeeper file /home/kaber/src/nf/ipsec/linux-2.6/net/ipv4/netfilter/ipt_policy.c
# 
# net/ipv4/netfilter/Makefile
#   2004/03/02 06:08:46+01:00 kaber@trash.net +1 -0
#   Add policy match
# 
# net/ipv4/netfilter/Kconfig
#   2004/03/02 06:08:46+01:00 kaber@trash.net +10 -0
#   Add policy match
# 
# include/linux/netfilter_ipv4/ipt_policy.h
#   2004/03/02 06:08:46+01:00 kaber@trash.net +0 -0
#   BitKeeper file /home/kaber/src/nf/ipsec/linux-2.6/include/linux/netfilter_ipv4/ipt_policy.h
# 
diff -Nru a/include/linux/netfilter_ipv4/ipt_policy.h b/include/linux/netfilter_ipv4/ipt_policy.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/linux/netfilter_ipv4/ipt_policy.h	Tue Mar  2 06:53:14 2004
@@ -0,0 +1,52 @@
+#ifndef _IPT_POLICY_H
+#define _IPT_POLICY_H
+
+#define POLICY_MAX_ELEM	4
+
+enum ipt_policy_flags
+{
+	POLICY_MATCH_IN		= 0x1,
+	POLICY_MATCH_OUT	= 0x2,
+	POLICY_MATCH_NONE	= 0x4,
+	POLICY_MATCH_STRICT	= 0x8,
+};
+
+enum ipt_policy_modes
+{
+	POLICY_MODE_TRANSPORT,
+	POLICY_MODE_TUNNEL
+};
+
+struct ipt_policy_spec
+{
+	u_int8_t	saddr:1,
+			daddr:1,
+			proto:1,
+			mode:1,
+			spi:1,
+			reqid:1;
+};
+
+struct ipt_policy_elem
+{
+	u_int32_t	saddr;
+	u_int32_t	smask;
+	u_int32_t	daddr;
+	u_int32_t	dmask;
+	u_int32_t	spi;
+	u_int32_t	reqid;
+	u_int8_t	proto;
+	u_int8_t	mode;
+
+	struct ipt_policy_spec	match;
+	struct ipt_policy_spec	invert;
+};
+
+struct ipt_policy_info
+{
+	struct ipt_policy_elem pol[POLICY_MAX_ELEM];
+	u_int16_t flags;
+	u_int16_t len;
+};
+
+#endif /* _IPT_POLICY_H */
diff -Nru a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
--- a/net/ipv4/netfilter/Kconfig	Tue Mar  2 06:53:14 2004
+++ b/net/ipv4/netfilter/Kconfig	Tue Mar  2 06:53:14 2004
@@ -127,6 +127,16 @@
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config IP_NF_MATCH_POLICY
+	tristate "IPsec policy match support"
+	depends on IP_NF_IPTABLES && XFRM
+	help
+	  Policy matching allows you to match packets based on the
+	  IPsec policy that was used during decapsulation/will
+	  be used during encapsulation.
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 config IP_NF_MATCH_MARK
 	tristate "netfilter MARK match support"
 	depends on IP_NF_IPTABLES
diff -Nru a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
--- a/net/ipv4/netfilter/Makefile	Tue Mar  2 06:53:14 2004
+++ b/net/ipv4/netfilter/Makefile	Tue Mar  2 06:53:14 2004
@@ -65,6 +65,7 @@
 obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o
 
 obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o
+obj-$(CONFIG_IP_NF_MATCH_POLICY) += ipt_policy.o
 
 # targets
 obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
diff -Nru a/net/ipv4/netfilter/ipt_policy.c b/net/ipv4/netfilter/ipt_policy.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/ipv4/netfilter/ipt_policy.c	Tue Mar  2 06:53:14 2004
@@ -0,0 +1,176 @@
+/* IP tables module for matching IPsec policy
+ *
+ * Copyright (c) 2004 Patrick McHardy, <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <net/xfrm.h>
+
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv4/ipt_policy.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_DESCRIPTION("IPtables IPsec policy matching module");
+MODULE_LICENSE("GPL");
+
+
+static inline int
+match_xfrm_state(struct xfrm_state *x, const struct ipt_policy_elem *e)
+{
+#define MISMATCH(x,y)	(e->match.x && ((e->x != (y)) ^ e->invert.x))
+
+	if (MISMATCH(saddr, x->props.saddr.a4 & e->smask) ||
+	    MISMATCH(daddr, x->id.daddr.a4 & e->dmask) ||
+	    MISMATCH(proto, x->id.proto) ||
+	    MISMATCH(mode, x->props.mode) ||
+	    MISMATCH(spi, x->id.spi) ||
+	    MISMATCH(reqid, x->props.reqid))
+		return 0;
+	return 1;
+}
+
+static int
+match_policy_in(const struct sk_buff *skb, const struct ipt_policy_info *info)
+{
+	const struct ipt_policy_elem *e;
+	struct sec_path *sp = skb->sp;
+	int strict = info->flags & POLICY_MATCH_STRICT;
+	int i, pos;
+
+	if (sp == NULL)
+		return -1;
+	if (strict && info->len != sp->len)
+		return 0;
+
+	for (i = sp->len - 1; i >= 0; i--) {
+		pos = strict ? i - sp->len + 1 : 0;
+		if (pos >= info->len)
+			return 0;
+		e = &info->pol[pos];
+
+		if (match_xfrm_state(sp->x[i].xvec, e)) {
+			if (!strict)
+				return 1;
+		} else if (strict)
+			return 0;
+	}
+
+	return strict ? 1 : 0;
+}
+
+static int
+match_policy_out(const struct sk_buff *skb, const struct ipt_policy_info *info)
+{
+	const struct ipt_policy_elem *e;
+	struct dst_entry *dst = skb->dst;
+	int strict = info->flags & POLICY_MATCH_STRICT;
+	int i, pos;
+
+	if (dst->xfrm == NULL)
+		return -1;
+
+	for (i = 0; dst && dst->xfrm; dst = dst->child, i++) {
+		pos = strict ? i : 0;
+		if (pos >= info->len)
+			return 0;
+		e = &info->pol[pos];
+
+		if (match_xfrm_state(dst->xfrm, e)) {
+			if (!strict)
+				return 1;
+		} else if (strict)
+			return 0;
+	}
+
+	return strict ? 1 : 0;
+}
+
+static int match(const struct sk_buff *skb,
+                 const struct net_device *in,
+                 const struct net_device *out,
+                 const void *matchinfo, int offset, int *hotdrop)
+{
+	const struct ipt_policy_info *info = matchinfo;
+	int ret;
+
+	if (info->flags & POLICY_MATCH_IN)
+		ret = match_policy_in(skb, info);
+	else
+		ret = match_policy_out(skb, info);
+
+	if (ret < 0) {
+		if (info->flags & POLICY_MATCH_NONE)
+			ret = 1;
+		else
+			ret = 0;
+	} else if (info->flags & POLICY_MATCH_NONE)
+		ret = 0;
+
+	return ret;
+}
+
+static int checkentry(const char *tablename, const struct ipt_ip *ip,
+                      void *matchinfo, unsigned int matchsize,
+                      unsigned int hook_mask)
+{
+	struct ipt_policy_info *info = matchinfo;
+
+	if (matchsize != IPT_ALIGN(sizeof(*info))) {
+		printk(KERN_ERR "ipt_policy: matchsize %u != %u\n",
+		       matchsize, IPT_ALIGN(sizeof(*info)));
+		return 0;
+	}
+	if (!(info->flags & (POLICY_MATCH_IN|POLICY_MATCH_OUT))) {
+		printk(KERN_ERR "ipt_policy: neither incoming nor "
+		                "outgoing policy selected\n");
+		return 0;
+	}
+	if (hook_mask & (1 << NF_IP_PRE_ROUTING | 1 << NF_IP_LOCAL_IN)
+	    && info->flags & POLICY_MATCH_OUT) {
+		printk(KERN_ERR "ipt_policy: output policy not valid in "
+		                "PRE_ROUTING and INPUT\n");
+		return 0;
+	}
+	if (hook_mask & (1 << NF_IP_POST_ROUTING | 1 << NF_IP_LOCAL_OUT)
+	    && info->flags & POLICY_MATCH_IN) {
+		printk(KERN_ERR "ipt_policy: input policy not valid in "
+		                "POST_ROUTING and OUTPUT\n");
+		return 0;
+	}
+	if (info->len > POLICY_MAX_ELEM) {
+		printk(KERN_ERR "ipt_policy: too many policy elements\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static struct ipt_match policy_match =
+{
+	.name		= "policy",
+	.match		= match,
+	.checkentry 	= checkentry,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ipt_register_match(&policy_match);
+}
+
+static void __exit fini(void)
+{
+	ipt_unregister_match(&policy_match);
+}
+
+module_init(init);
+module_exit(fini);

--------------060704090701060106030300
Content-Type: text/x-patch;
 name="libipt_policy.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="libipt_policy.diff"

diff -urN a/extensions/.policy-test b/extensions/.policy-test
--- a/extensions/.policy-test	2004-02-23 20:39:37.000000000 +0100
+++ b/extensions/.policy-test	2004-03-04 21:41:39.000000000 +0100
@@ -1,2 +1,3 @@
 #!/bin/sh
-[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_policy.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_policy.h ] && echo policy
+#
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_policy.h ] && echo policy
diff -urN a/extensions/libipt_policy.c b/extensions/libipt_policy.c
--- a/extensions/libipt_policy.c	2004-02-23 20:38:37.000000000 +0100
+++ b/extensions/libipt_policy.c	2004-03-04 21:41:32.000000000 +0100
@@ -1,3 +1,4 @@
+/* Shared library add-on to iptables to add policy support. */
 #include <stdio.h>
 #include <netdb.h>
 #include <string.h>
@@ -14,72 +15,82 @@
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_policy.h>
 
+/* 
+ * HACK: global pointer to current matchinfo for making
+ * final checks and adjustments in final_check.
+ */
+static struct ipt_policy_info *policy_info;
+
 static void help(void)
 {
 	printf(
 "policy v%s options:\n"
-"[!] --reqid reqid		Match reqid\n"
-"[!] --spi spi			Match SPI\n"
-"[!] --proto proto		Match protocol (ah/esp/ipcomp)\n"
-"[!] --mode mode 		Match mode (transport/tunnel)\n"
-"[!] --local addr/mask		Match local tunnel endpoint\n"
-"[!] --remote addr/mask		Match remote tunnel endpoint\n"
-"  --path 			Match path instead of single element at\n"
-"				any position\n"
-"  --next 			Begin next element in path\n",
+"  --dir in|out			match policy applied during decapsulation/\n"
+"				policy to be applied during encapsulation\n"
+"  --pol none|ipsec		match policy\n"
+"  --strict 			match entire policy instead of single element\n"
+"				at any position\n"
+"[!] --reqid reqid		match reqid\n"
+"[!] --spi spi			match SPI\n"
+"[!] --proto proto		match protocol (ah/esp/ipcomp)\n"
+"[!] --mode mode 		match mode (transport/tunnel)\n"
+"[!] --tunnel-src addr/mask	match tunnel source\n"
+"[!] --tunnel-dst addr/mask	match tunnel destination\n"
+"  --next 			begin next element in policy\n",
 	IPTABLES_VERSION);
 }
 
-static struct option opts[] = {
+static struct option opts[] =
+{
 	{
-		.name		= "reqid",
+		.name		= "dir",
 		.has_arg	= 1,
-		.flag		= 0,
 		.val		= '1',
 	},
 	{
-		.name		= "spi",
+		.name		= "pol",
 		.has_arg	= 1,
-		.flag		= 0,
-		.val		= '2'
+		.val		= '2',
 	},
 	{
-		.name		= "local",
-		.has_arg	= 1,
-		.flag		= 0,
+		.name		= "strict",
 		.val		= '3'
 	},
 	{
-		.name		= "remote",
+		.name		= "reqid",
 		.has_arg	= 1,
-		.flag		= 0,
-		.val		= '4'
+		.val		= '4',
 	},
 	{
-		.name		= "proto",
+		.name		= "spi",
 		.has_arg	= 1,
-		.flag		= 0,
 		.val		= '5'
 	},
 	{
-		.name		= "mode",
+		.name		= "tunnel-src",
 		.has_arg	= 1,
-		.flag		= 0,
 		.val		= '6'
 	},
 	{
-		.name		= "path",
-		.has_arg	= 0,
-		.flag		= 0,
+		.name		= "tunnel-dst",
+		.has_arg	= 1,
 		.val		= '7'
 	},
 	{
-		.name		= "next",
-		.has_arg	= 0,
-		.flag		= 0,
+		.name		= "proto",
+		.has_arg	= 1,
 		.val		= '8'
 	},
-	{ 0 }
+	{
+		.name		= "mode",
+		.has_arg	= 1,
+		.val		= '9'
+	},
+	{
+		.name		= "next",
+		.val		= 'a'
+	},
+	{ }
 };
 
 static void init(struct ipt_entry_match *m, unsigned int *nfcache)
@@ -87,154 +98,201 @@
 	*nfcache |= NFC_UNKNOWN;
 }
 
+static int parse_direction(char *s)
+{
+	if (strcmp(s, "in") == 0)
+		return POLICY_MATCH_IN;
+	if (strcmp(s, "out") == 0)
+		return POLICY_MATCH_OUT;
+	exit_error(PARAMETER_PROBLEM, "policy_match: invalid dir `%s'", s);
+}
+
+static int parse_policy(char *s)
+{
+	if (strcmp(s, "none") == 0)
+		return POLICY_MATCH_NONE;
+	if (strcmp(s, "ipsec") == 0)
+		return 0;
+	exit_error(PARAMETER_PROBLEM, "policy match: invalid policy `%s'", s);
+}
+
 static int parse_mode(char *s)
 {
 	if (strcmp(s, "transport") == 0)
-		return SECPATH_MODE_TRANSPORT;
+		return POLICY_MODE_TRANSPORT;
 	if (strcmp(s, "tunnel") == 0)
-		return SECPATH_MODE_TUNNEL;
-	return -EINVAL;
+		return POLICY_MODE_TUNNEL;
+	exit_error(PARAMETER_PROBLEM, "policy match: invalid mode `%s'", s);
 }
 
-static int parse(int c, char **argv, int invert, unsigned int *i,
+static int parse(int c, char **argv, int invert, unsigned int *flags,
                  const struct ipt_entry *entry,
                  unsigned int *nfcache,
                  struct ipt_entry_match **match)
 {
 	struct ipt_policy_info *info = (void *)(*match)->data;
-	struct ipt_policy_elem *e = &info->path[*i];
+	struct ipt_policy_elem *e = &info->pol[info->len];
 	struct in_addr *addr = NULL, mask;
-	struct protoent *p;
 	unsigned int naddr = 0;
-	int mode, proto;
+	int mode;
 
 	check_inverse(optarg, &invert, &optind, 0);
 
 	switch (c) {
 	case '1':
+		if (info->flags & (POLICY_MATCH_IN|POLICY_MATCH_OUT))
+			exit_error(PARAMETER_PROBLEM,
+			           "policy match: double --dir option");
+		if (invert)
+			exit_error(PARAMETER_PROBLEM,
+			           "policy match: can't invert --dir option");
+
+		info->flags |= parse_direction(argv[optind-1]);
+		break;
+	case '2':
+		if (invert)
+			exit_error(PARAMETER_PROBLEM,
+			           "policy match: can't invert --policy option");
+
+		info->flags |= parse_policy(argv[optind-1]);
+		break;
+	case '3':
+		if (info->flags & POLICY_MATCH_STRICT)
+			exit_error(PARAMETER_PROBLEM,
+			           "policy match: double --strict option");
+
+		if (invert)
+			exit_error(PARAMETER_PROBLEM,
+			           "policy match: can't invert --strict option");
+
+		info->flags |= POLICY_MATCH_STRICT;
+		break;
+	case '4':
 		if (e->match.reqid)
 			exit_error(PARAMETER_PROBLEM,
-			           "Can't specify --reqid twice");
+			           "policy match: double --reqid option");
 
 		e->match.reqid = 1;
 		e->invert.reqid = invert;
 		e->reqid = strtol(argv[optind-1], NULL, 10);
 		break;
-	case '2':
+	case '5':
 		if (e->match.spi)
 			exit_error(PARAMETER_PROBLEM,
-			           "Can't specify --spi twice");
+			           "policy match: double --spi option");
 		
 		e->match.spi = 1;
 		e->invert.spi = invert;
 		e->spi = strtol(argv[optind-1], NULL, 0x10);
 		break;
-	case '3':
-		if (e->match.daddr)
-			exit_error(PARAMETER_PROBLEM,
-			           "Can't specify --local twice");
-		
-		parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
-		if (naddr > 1)
-			exit_error(PARAMETER_PROBLEM,
-			           "Multiple IP addresses are not allowed");
-
-		e->match.daddr = 1;
-		e->invert.daddr = invert;
-		e->daddr = addr[0].s_addr;
-		e->dmask = mask.s_addr;
-		break;
-	case '4':
+	case '6':
 		if (e->match.saddr)
 			exit_error(PARAMETER_PROBLEM,
-			           "Can't specify --remote twice");
+			           "policy match: double --tunnel-src option");
 
 		parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
 		if (naddr > 1)
 			exit_error(PARAMETER_PROBLEM,
-			           "Multiple IP addresses are not allowed");
+			           "policy match: name resolves to multiple IPs");
 
 		e->match.saddr = 1;
 		e->invert.saddr = invert;
 		e->saddr = addr[0].s_addr;
 		e->smask = mask.s_addr;
                 break;
-	case '5':
-		if (e->match.proto)
+	case '7':
+		if (e->match.daddr)
+			exit_error(PARAMETER_PROBLEM,
+			           "policy match: double --tunnel-dst option");
+
+		parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
+		if (naddr > 1)
 			exit_error(PARAMETER_PROBLEM,
-			           "Can't specify --proto twice");
+			           "policy match: name resolves to multiple IPs");
 
-		p = getprotobyname(argv[optind-1]);
-		if (p != NULL)
-			proto = p->p_proto;
-		else if (!(proto = atoi(argv[optind-1])))
+		e->match.daddr = 1;
+		e->invert.daddr = invert;
+		e->daddr = addr[0].s_addr;
+		e->dmask = mask.s_addr;
+		break;
+	case '8':
+		if (e->match.proto)
 			exit_error(PARAMETER_PROBLEM,
-			           "Unknown protocol `%s'\n",
-			           argv[optind-1]);
+			           "policy match: double --proto option");
 
+		e->proto = parse_protocol(argv[optind-1]);
+		if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP &&
+		    e->proto != IPPROTO_COMP)
+			exit_error(PARAMETER_PROBLEM,
+			           "policy match: protocol must ah/esp/ipcomp");
 		e->match.proto = 1;
 		e->invert.proto = invert;
-		e->proto = proto;
 		break;
-	case '6':
+	case '9':
 		if (e->match.mode)
 			exit_error(PARAMETER_PROBLEM,
-			           "Can't specify --mode twice");
+			           "policy match: double --mode option");
 		
 		mode = parse_mode(argv[optind-1]);
-		if (mode < 0)
-			exit_error(PARAMETER_PROBLEM,
-			           "Unknown mode `%s'\n",
-			           argv[optind-1]);
-
 		e->match.mode = 1;
 		e->invert.mode = invert;
 		e->mode = mode;
 		break;
-	case '7':
-		if (info->flags & MATCH_PATH)
-			exit_error(PARAMETER_PROBLEM,
-			           "Can't specify --path twice");
-
+	case 'a':
 		if (invert)
 			exit_error(PARAMETER_PROBLEM,
-			           "Can't invert `--path' option");
+			           "policy match: can't invert --next option");
 
-		info->flags |= MATCH_PATH;
-		break;
-	case '8':
-		if (invert)
+		if (++info->len == POLICY_MAX_ELEM)
 			exit_error(PARAMETER_PROBLEM,
-			           "Can't invert `--next' option");
-
-		if (++(*i) == SECPATH_MAX_DEPTH)
-			exit_error(PARAMETER_PROBLEM,
-			           "Maximum path depth reached");
-
+			           "policy match: maximum policy depth reached");
 		break;
 	default:
 		return 0;
 	}
 
-	info->len = *i + 1;
+	policy_info = info;
 	return 1;
 }
 
 static void final_check(unsigned int flags)
 {
-	return;
-}
+	struct ipt_policy_info *info = policy_info;
+	struct ipt_policy_elem *e;
+	int i;
 
-static void print_proto(char *prefix, u_int8_t proto, int numeric)
-{
-	struct protoent *p = NULL;
+	if (info == NULL)
+		exit_error(PARAMETER_PROBLEM,
+		           "policy match: no parameters given");
 
-	if (!numeric)
-		p = getprotobynumber(proto);
-	if (p == NULL)
-		printf("%sproto %u ", prefix, proto);
-	else
-		printf("%sproto %s ", prefix, p->p_name);
+	if (!(info->flags & (POLICY_MATCH_IN|POLICY_MATCH_OUT)))
+		exit_error(PARAMETER_PROBLEM,
+		           "policy match: neither --in nor --out specified");
+
+	if (info->flags & POLICY_MATCH_NONE) {
+		if (info->flags & POLICY_MATCH_STRICT)
+			exit_error(PARAMETER_PROBLEM,
+			           "policy match: policy none but --strict given");
+
+		if (info->len != 0)
+			exit_error(PARAMETER_PROBLEM,
+			           "policy match: policy none but policy given");
+	} else
+		info->len++;	/* increase len by 1, no --next after last element */
+
+	if (!(info->flags & POLICY_MATCH_STRICT) && info->len > 1)
+		exit_error(PARAMETER_PROBLEM,
+		           "policy match: multiple elements but no --strict");
+
+	for (i = 0; i < info->len; i++) {
+		e = &info->pol[i];
+		if ((e->match.saddr || e->match.daddr)
+		    && ((e->mode == POLICY_MODE_TUNNEL && e->invert.mode) ||
+		        (e->mode == POLICY_MODE_TRANSPORT && !e->invert.mode)))
+			exit_error(PARAMETER_PROBLEM,
+			           "policy match: --tunnel-src/--tunnel-dst "
+			           "is only valid in tunnel mode");
+	}
 }
 
 static void print_mode(char *prefix, u_int8_t mode, int numeric)
@@ -242,10 +300,10 @@
 	printf("%smode ", prefix);
 
 	switch (mode) {
-	case SECPATH_MODE_TRANSPORT:
+	case POLICY_MODE_TRANSPORT:
 		printf("transport ");
 		break;
-	case SECPATH_MODE_TUNNEL:
+	case POLICY_MODE_TUNNEL:
 		printf("tunnel ");
 		break;
 	default:
@@ -273,7 +331,7 @@
 	}
 	if (e->match.proto) {
 		PRINT_INVERT(e->invert.proto);
-		print_proto(prefix, e->proto, numeric);
+		printf("%sproto %s ", prefix, proto_to_name(e->proto, numeric));
 	}
 	if (e->match.mode) {
 		PRINT_INVERT(e->invert.mode);
@@ -281,18 +339,34 @@
 	}
 	if (e->match.daddr) {
 		PRINT_INVERT(e->invert.daddr);
-		printf("%slocal %s%s ", prefix,
+		printf("%stunnel-dst %s%s ", prefix,
 		       addr_to_dotted((struct in_addr *)&e->daddr),
 		       mask_to_dotted((struct in_addr *)&e->dmask));
 	}
 	if (e->match.saddr) {
 		PRINT_INVERT(e->invert.saddr);
-		printf("%sremote %s%s ", prefix,
+		printf("%stunnel-src %s%s ", prefix,
 		       addr_to_dotted((struct in_addr *)&e->saddr),
 		       mask_to_dotted((struct in_addr *)&e->smask));
 	}
 }
 
+static void print_flags(char *prefix, const struct ipt_policy_info *info)
+{
+	if (info->flags & POLICY_MATCH_IN)
+		printf("%sdir in ", prefix);
+	else
+		printf("%sdir out ", prefix);
+
+	if (info->flags & POLICY_MATCH_NONE)
+		printf("%spol none ", prefix);
+	else
+		printf("%spol ipsec ", prefix);
+
+	if (info->flags & POLICY_MATCH_STRICT)
+		printf("%sstrict ", prefix);
+}
+
 static void print(const struct ipt_ip *ip,
                   const struct ipt_entry_match *match,
 		  int numeric)
@@ -300,14 +374,12 @@
 	const struct ipt_policy_info *info = (void *)match->data;
 	unsigned int i;
 
-	printf ("policy match ");
-	if (info->flags & MATCH_PATH)
-		printf("path ");
-
+	printf("policy match ");
+	print_flags("", info);
 	for (i = 0; i < info->len; i++) {
 		if (info->len > 1)
 			printf("[%u] ", i);
-		print_entry("", &info->path[i], numeric);
+		print_entry("", &info->pol[i], numeric);
 	}
 
 	printf("\n");
@@ -318,11 +390,9 @@
 	const struct ipt_policy_info *info = (void *)match->data;
 	unsigned int i;
 
-	if (info->flags & MATCH_PATH)
-		printf("--path ");
-
+	print_flags("--", info);
 	for (i = 0; i < info->len; i++) {
-		print_entry("--", &info->path[i], 0);
+		print_entry("--", &info->pol[i], 0);
 		if (i + 1 < info->len)
 			printf("--next ");
 	}
@@ -332,18 +402,17 @@
 
 struct iptables_match policy =
 {
-	NULL,
-	"policy",
-	IPTABLES_VERSION,
-	IPT_ALIGN(sizeof(struct ipt_policy_info)),
-	IPT_ALIGN(sizeof(struct ipt_policy_info)),
-	&help,
-	&init,
-	&parse,
-	&final_check,
-	&print,
-	&save,
-	opts
+	.name		= "policy",
+	.version	= IPTABLES_VERSION,
+	.size		= IPT_ALIGN(sizeof(struct ipt_policy_info)),
+	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_policy_info)),
+	.help		= help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.save		= save,
+	.extra_opts	= opts
 };
 
 void _init(void)
diff -urN a/include/iptables.h b/include/iptables.h
--- a/include/iptables.h	2004-03-04 21:37:40.000000000 +0100
+++ b/include/iptables.h	2004-03-04 21:38:36.000000000 +0100
@@ -122,6 +122,7 @@
 extern void register_match(struct iptables_match *me);
 extern void register_target(struct iptables_target *me);
 
+extern char *proto_to_name(u_int8_t proto, int nolookup);
 extern struct in_addr *dotted_to_addr(const char *dotted);
 extern char *addr_to_dotted(const struct in_addr *addrp);
 extern char *addr_to_anyname(const struct in_addr *addr);
diff -urN a/iptables.c b/iptables.c
--- a/iptables.c	2004-02-24 18:28:50.000000000 +0100
+++ b/iptables.c	2004-03-04 21:39:18.000000000 +0100
@@ -235,11 +235,12 @@
 	{ "icmp", IPPROTO_ICMP },
 	{ "esp", IPPROTO_ESP },
 	{ "ah", IPPROTO_AH },
+	{ "ipcomp", IPPROTO_COMP },
 	{ "sctp", IPPROTO_SCTP },
 	{ "all", 0 },
 };
 
-static char *
+char *
 proto_to_name(u_int8_t proto, int nolookup)
 {
 	unsigned int i;

--------------060704090701060106030300
Content-Type: text/plain;
 name="policy-test"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="policy-test"

#! /bin/bash

PATH="/usr/local/sbin"

iptables -F


iptables -A INPUT -m policy --dir in --pol none
iptables -A INPUT -m policy --dir in --pol ipsec
iptables -A INPUT -m policy --dir in --pol ipsec --mode tunnel
iptables -A INPUT -m policy --dir in --pol ipsec --mode transport
iptables -A INPUT -m policy --dir in --pol ipsec --proto ah
iptables -A INPUT -m policy --dir in --pol ipsec --proto esp
iptables -A INPUT -m policy --dir in --pol ipsec --strict --mode tunnel
iptables -A INPUT -m policy --dir in --pol ipsec --strict --mode transport
iptables -A INPUT -m policy --dir in --pol ipsec --strict --proto ah
iptables -A INPUT -m policy --dir in --pol ipsec --strict --proto esp
iptables -A INPUT -m policy --dir in --pol ipsec --strict --proto esp --next --proto ah
iptables -A INPUT -m policy --dir in --pol ipsec --strict --proto esp --mode tunnel --tunnel-src 192.168.0.1 --next --proto ah --mode transport
iptables -A INPUT -m policy --dir in --pol ipsec --strict --proto esp --mode tunnel --tunnel-src 192.168.0.1 --tunnel-dst 192.168.0.23 --next --proto ah --mode transport


iptables -A OUTPUT -m policy --dir out --pol none
iptables -A OUTPUT -m policy --dir out --pol ipsec
iptables -A OUTPUT -m policy --dir out --pol ipsec --mode tunnel
iptables -A OUTPUT -m policy --dir out --pol ipsec --mode transport
iptables -A OUTPUT -m policy --dir out --pol ipsec --proto ah
iptables -A OUTPUT -m policy --dir out --pol ipsec --proto esp
iptables -A OUTPUT -m policy --dir out --pol ipsec --strict --mode tunnel
iptables -A OUTPUT -m policy --dir out --pol ipsec --strict --mode transport
iptables -A OUTPUT -m policy --dir out --pol ipsec --strict --proto ah
iptables -A OUTPUT -m policy --dir out --pol ipsec --strict --proto esp
iptables -A OUTPUT -m policy --dir out --pol ipsec --strict --proto esp --next --proto ah
iptables -A OUTPUT -m policy --dir out --pol ipsec --strict --proto esp --mode tunnel --tunnel-dst 192.168.0.0/24 --next --proto ah
iptables -A OUTPUT -m policy --dir out --pol ipsec --strict --proto esp --mode tunnel --tunnel-dst ! 192.168.0.0/24 --next --proto ah
iptables -A OUTPUT -m policy --dir out --pol ipsec --strict --proto esp --mode tunnel --tunnel-dst 192.168.0.1 --next --proto ah --mode transport


--------------060704090701060106030300--