Authors of tunnel (or encapsulation) drivers should follow two simple rules for the 2.4 kernel (as do the drivers inside the kernel, like net/ipv4/ipip.c):
Otherwise: the NAT code will use the old connection tracking information to mangle the packet, with bad consequences.
Otherwise: the user will not be able to filter as they expect to with tunnels.
The canonical way to do the first is to insert code like the following before you wrap or unwrap the packet:
/* 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
Usually, all you need to do for the second, is to find where the newly encapsulated packet goes into "ip_send()", and replace it with something like:
/* Send "new" packet from local host */
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, ip_send);
Following these rules means that the person setting up the packet filtering rules on the tunnel box will see something like the following sequence for a packet being tunnelled:
And for the reply packet: