Transparent cache, taking transparent web cluster for example, seems like below
topology or deployment:
Given client machines make ipvs director default gateway, http requests toward
internet web sites from the clients forward through the ipvs director.
Obviously, the director must be enable to capture those packets which are
involved in some virtual service defined in the ipvs rule table, though they
exist in FORWARD chain which go beyond the power control of ipvs, because the
only outside-inside function entrance is ip_vs_in() which only hooks in INPUT
chain, right? As long as ip_vs_in() handles these packets, ipvs can finally
forward (VS/DR) them to the backend real servers. The real servers use the
REDIRECT iptable rule to let local squid adopt them, and at last, client
machines get the correct http responses.
LVS HOWTO provides two ways to meet our need, but I found they don't work.
1. define a route in local route table (in "17.3. How you use TP" chapter)
I test this way in Redhat AS4.0_update2 which uses 2.6.x kernel, but nothing
magic happens, :-( Maybe 2.6.x kernel does not support this trick.
Even this can work, but how can you capture all packets to the whole internet
web sites in your local route table? :-)
2. REDIRECT iptable rule
Let's see how REDIRECT rule work in 2.6.x kernel first.
Given such a rule configured on ipvs director:
# iptables -t mangle -A PREROUTING -p tcp -s <internal network> --dport 80 -j
REDIRECT --to-ports 3128
When a packet [cip:cport -> website_ip:80] comes in, netfilter redirects it
from PREROUTING chain to INPUT chain, and it becomes [cip:cport ->
localhost_ip:3128], and then it's handled by squid (with transparent proxy
settings). Squid gets the webpage from cache or from internet, and send back
the webpage to the client. When the response packet [localhost_ip:3128 ->
cip:cport] flows at the end of POSTROUTING chain, netfilter recovers its
origin, then the packet becomes [website_ip:80 -> cip:cport] and go toward the
client machine on the wire.
We can see that, netfilter MASQUERADEs the packet both on PREROUTING and
POSTROUTING chains. However, it is useless to ipvs director.
First, the packets which ip_vs_in() handles own the unique localhost ip as the
destination ip, and that make those schedule algorithms like lblc, lblcr, dh
(dedicated to cache cluster) useless!
Second, ipvs registers ip_vs_post_routing(), which hooks on POSTROUTING chain,
with a priority of NF_IP_PRI_NAT_SRC-1, which means it is called by netfilter
core before NAT hooks like REDIRECT. This function returns NF_STOP for those
packets which ipvs_property is set, and REDIRECT has no chance to MASQUERADE
those packets. Finally, client receives the response, but it dost not match the
request on the orign ip:port!
I think out a solution for the support of TP in ipvs natively, and make a patch:
--- linux-2.6.16/net/ipv4/ipvs/ip_vs_core.c 2006-03-20 13:53:29.000000000
+0800
+++ linux-2.6.16.new/net/ipv4/ipvs/ip_vs_core.c 2006-11-21
18:28:35.000000000 +0800
@@ -23,6 +23,8 @@
* Changes:
* Paul `Rusty' Russell properly handle non-linear skbs
* Harald Welte don't use nfcache
+ * Jinhua Luo redirect packets with fwmark on
NF_IP_FORWARD chain
+ * to ip_vs_in(), mainly for transparent
cache cluster
*
*/
@@ -1060,6 +1062,16 @@
return ip_vs_in_icmp(pskb, &r, hooknum);
}
+static unsigned int
+ip_vs_forward_with_fwmark(unsigned int hooknum, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ if ((*pskb)->ipvs_property || ! (*pskb)->nfmark)
+ return NF_ACCEPT;
+
+ return ip_vs_in(hooknum, pskb, in, out, okfn);
+}
/* After packet filtering, forward packet through VS/DR, VS/TUN,
or VS/NAT(change destination), so that filtering rules can be
@@ -1072,6 +1084,14 @@
.priority = 100,
};
+static struct nf_hook_ops ip_vs_forward_with_fwmark_ops = {
+ .hook = ip_vs_forward_with_fwmark,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_IP_FORWARD,
+ .priority = 101,
+};
+
/* After packet filtering, change source only for VS/NAT */
static struct nf_hook_ops ip_vs_out_ops = {
.hook = ip_vs_out,
@@ -1150,9 +1170,17 @@
goto cleanup_postroutingops;
}
+ ret = nf_register_hook(&ip_vs_forward_with_fwmark_ops);
+ if (ret < 0) {
+ IP_VS_ERR("can't register forward_with_fwmark hook.\n");
+ goto cleanup_forwardicmpops;
+ }
+
IP_VS_INFO("ipvs loaded.\n");
return ret;
+ cleanup_forwardicmpops:
+ nf_unregister_hook(&ip_vs_forward_icmp_ops);
cleanup_postroutingops:
nf_unregister_hook(&ip_vs_post_routing_ops);
cleanup_outops:
@@ -1172,6 +1200,7 @@
static void __exit ip_vs_cleanup(void)
{
+ nf_unregister_hook(&ip_vs_forward_with_fwmark_ops);
nf_unregister_hook(&ip_vs_forward_icmp_ops);
nf_unregister_hook(&ip_vs_post_routing_ops);
nf_unregister_hook(&ip_vs_out_ops);
Here, I redirect the packets with fwmark to ip_vs_in() on the FORWARD chain,
and ip_vs_in() will handle the packets which are marked by iptables to indicate
transparent cache virtual service, but ignore other packets (let them continue
to flow on the FORWARD chain).
Now you can use ipvs to deploy TP at ease:
@ ipvs director
# sysctl -w net.ipv4.ip_forward=1
# iptables -t mangle -A FORWARD -p tcp -s <internal network> --dport 80 -j MARK
--set-mark 1
# ipvsadm -A -f 1 -s lblcr
# ipvsadm -a -f 1 -r RS1
# ipvsadm -a -f 1 -r RS2
@ RS
# iptables -t mangle -A PREROUTING -p tcp -s <internal network> --dport 80 -j
REDIRECT --to-ports 3128
# cat >> /etc/squid/squid.conf << EOF
httpd_accel_host virtual
httpd_accel_port 80
httpd_accel_with_proxy on
httpd_accel_uses_host_header on
# /etc/init.d/squid start
|