Subject: |
Patch for CIPE to use NAT with iptables |
From: |
Rik Faith <faith,AT,alephnull,DOT,com> |
Date: |
Wed, 31 Oct 2001 15:35:16 +0100 |
PROBLEM
Consider using CIPE with the following network topology:
A <---LAN---> FW <---WAN---> PEER
^
|- - - CIPE - - -> INT
A is a machine behind your firewall
FW is your firewall, running CIPE
PEER is the CIPE peer listed in /etc/cipe/options
INT is an internal machine on the VPN network that CIPE creates
LAN is the network behind FW
WAN is the connection from the FW to the Internet
CIPE is a virtual network that's created by tunneling packets from
FW through PEER
WANIP is the IP address of your WAN interface on FW
CIPEIP is the IP address of your CIPE interface ("me") on FW
You would like to use a rule like this to provide access from A to
INT:
iptables -A POSTROUTING -j SNAT -t nat -o cipcb0 --to-source CIPEIP
When a packet is sent from A to INT, the following currently
happens [using (source,destination) notation and CIPE 1.5.2]:
1) (A,INT) is routed to cipcb0
2) The POSTROUTING rule changes this packet to (CIPEIP,INT)
3) The cipcb kernel module encrypts this packet and encapsulates
it with (WANIP,PEER), injecting it back into the kernel with
the ip_send(skb) call.
4) The POSTROUTING rule changes this packet to (CIPEIP,PEER),
causing the PEER machine to send the reply to CIPEIP instead
of WANIP. So, you never see the return packet.
SOLUTION SPACE
The solution to this problem is to prevent the POSTROUTING mangling
that happens in (4) above. My first attempt to do this involved a
kernel _hack_ to the lowest-level netfilter code in ip_nat_core.c.
I sent this to Rusty, who replied with a better patch (against the
CIPE code) that makes CIPE work well with current netfilter support.
I've attached Rusty's email below -- it contains a patch (that I've
tested with CIPE 1.5.2 and Linux 2.4.13-ac4) and some explanation of
the solution.
To:
Rik Faith <faith,AT,alephnull,DOT,com>
Subject:
Re: netfilter+CIPE+NAT
From:
Rusty Russell <rusty,AT,rustcorp,DOT,com,DOT,au>
Date:
Tue, 30 Oct 2001 10:23:54 +1100
Cc:
Olaf Titz <olaf,AT,bigred,DOT,inka,DOT,de>
In message <15318.53171.109991.850849,AT,light,DOT,alephnull,DOT,com> you
write:
> Rusty,
> Thanks for your previous help with netfilter. I have a new
> problem. Have you seen:
> http://sites.inka.de/bigred/archive/cipe-l/2001-08/msg00117.html
Hi Rik,
Looked at the CIPE source, and it doesn't follow the netfilter
requirements for tunnels (not surprisingly, since they didn't exist
before 2.3.x). They are:
MUST:
o Release skb->nfct if you're going to make the packet
unrecognisable (ie. decapsulating/encapsulating). You
don't need to do this if you unwrap it into a *new* skb,
but if you're going to do it in place, you must do this.
Otherwise: the NAT code will use the old connection
tracking information to mangle the packet, with bad
consequences.
SHOULD:
o Make sure the encapsulated packets go through the LOCAL_OUT
hook, and decapsulated packets go through the PRE_ROUTING
hook (most tunnels use ip_rcv(), which does this for you).
Otherwise: the user will not be able to filter as they
expect to with tunnels.
Olaf, I've got a patch (untested!): does this make sense to you?
Rusty.
--
Premature optmztion is rt of all evl. --DK
diff -ur cipe-1.5.2/cipe/output.c cipe-1.5.2-netfilter/cipe/output.c
--- cipe-1.5.2/cipe/output.c Wed May 2 07:23:42 2001
+++ cipe-1.5.2-netfilter/cipe/output.c Mon Oct 29 16:04:30 2001
@@ -20,6 +20,11 @@
#include <linux/if_arp.h>
#include <linux/socket.h>
#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#include <linux/netfilter_ipv4.h>
+#else
+#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
+#endif
#ifdef DEBUG
@@ -83,6 +88,13 @@
}
#endif
+/* Need this wrapper because NF_HOOK takes the function address, and
+ ip_send was declared "extern inline" in the vague past. --RR */
+static inline int do_ip_send(struct sk_buff *skb)
+{
+ return ip_send(skb);
+}
+
int cipe_xmit(struct sk_buff *skb, struct NET_DEVICE *dev)
{
struct cipe *tunnel = (struct cipe*)(dev->priv);
@@ -149,6 +161,16 @@
goto tx_error_out;
#endif
+ /* Tell the netfilter framework that this packet is not the
+ same as the one before! */
+#ifdef CONFIG_NETFILTER
+ nf_conntrack_put(skb->nfct);
+ skb->nfct = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug = 0;
+#endif
+#endif
+
#if 0
dprintk(DEB_OUT, (KERN_DEBUG "routing dst=%s src=%s tos=%x oif=%d\n",
cipe_ntoa(0, dst), cipe_ntoa(1, tunnel->myaddr),
@@ -322,7 +344,10 @@
if (cipe_debug&DEB_PKOU)
cipe_dump_packet("sending", skb, 0);
#endif
- ip_send(skb);
+
+ /* Send "new" packet from local host */
+ NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
+ do_ip_send);
tunnel->recursion--;
return 0;