ipvs: load balance ipv6 connections from a local process
authorSimon Horman <horms@verge.net.au>
Fri, 5 Sep 2008 01:17:14 +0000 (11:17 +1000)
committerSimon Horman <horms@verge.net.au>
Fri, 5 Sep 2008 01:17:14 +0000 (11:17 +1000)
This allows IPVS to load balance IPv6 connections made by a local process.
For example a proxy server running locally.

External client --> pound:443 -> Local:443 --> IPVS:80 --> RealServer

This is an extenstion to the IPv4 work done in this area
by Siim Põder and Malcolm Turnbull.

Cc: Siim Põder <siim@p6drad-teel.net>
Cc: Malcolm Turnbull <malcolm@loadbalancer.org>
Signed-off-by: Simon Horman <horms@verge.net.au>
net/ipv4/ipvs/ip_vs_core.c

index 26e3d99bbeea720f69aa8b69229a41c980b99c1a..05797a5ce1572c22caec46b305c041343e1ae59f 100644 (file)
@@ -654,8 +654,9 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
 /* Handle relevant response ICMP messages - forward to the right
  * destination host. Used for NAT and local client.
  */
-static int handle_response_icmp(struct sk_buff *skb, struct iphdr *iph,
-                               struct iphdr *cih, struct ip_vs_conn *cp,
+static int handle_response_icmp(int af, struct sk_buff *skb,
+                               union nf_inet_addr *snet,
+                               __u8 protocol, struct ip_vs_conn *cp,
                                struct ip_vs_protocol *pp,
                                unsigned int offset, unsigned int ihl)
 {
@@ -669,18 +670,22 @@ static int handle_response_icmp(struct sk_buff *skb, struct iphdr *iph,
        /* Ensure the checksum is correct */
        if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
                /* Failed checksum! */
-               IP_VS_DBG(1,
-                         "Forward ICMP: failed checksum from %d.%d.%d.%d!\n",
-                         NIPQUAD(iph->saddr));
+               IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
+                             IP_VS_DBG_ADDR(af, snet));
                goto out;
        }
 
-       if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol)
+       if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol)
                offset += 2 * sizeof(__u16);
        if (!skb_make_writable(skb, offset))
                goto out;
 
-       ip_vs_nat_icmp(skb, pp, cp, 1);
+#ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+               ip_vs_nat_icmp_v6(skb, pp, cp, 1);
+       else
+#endif
+               ip_vs_nat_icmp(skb, pp, cp, 1);
 
        /* do the statistics and put it back */
        ip_vs_out_stats(cp, skb);
@@ -708,6 +713,7 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related)
        struct ip_vs_conn *cp;
        struct ip_vs_protocol *pp;
        unsigned int offset, ihl;
+       union nf_inet_addr snet;
 
        *related = 1;
 
@@ -766,7 +772,9 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related)
        if (!cp)
                return NF_ACCEPT;
 
-       return handle_response_icmp(skb, iph, cih, cp, pp, offset, ihl);
+       snet.ip = iph->saddr;
+       return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
+                                   pp, offset, ihl);
 }
 
 #ifdef CONFIG_IP_VS_IPV6
@@ -779,7 +787,8 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related)
        struct ip_vs_iphdr ciph;
        struct ip_vs_conn *cp;
        struct ip_vs_protocol *pp;
-       unsigned int offset, verdict;
+       unsigned int offset;
+       union nf_inet_addr snet;
 
        *related = 1;
 
@@ -838,40 +847,9 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related)
        if (!cp)
                return NF_ACCEPT;
 
-       verdict = NF_DROP;
-
-       if (IP_VS_FWD_METHOD(cp) != 0) {
-               IP_VS_ERR("shouldn't reach here, because the box is on the "
-                         "half connection in the tun/dr module.\n");
-       }
-
-       /* Ensure the checksum is correct */
-       if (!skb_csum_unnecessary(skb)
-           && ip_vs_checksum_complete(skb, sizeof(struct ipv6hdr))) {
-               /* Failed checksum! */
-               IP_VS_DBG(1, "Forward ICMPv6: failed checksum from "
-                         NIP6_FMT "!\n",
-                         NIP6(iph->saddr));
-               goto out;
-       }
-
-       if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr)
-               offset += 2 * sizeof(__u16);
-       if (!skb_make_writable(skb, offset))
-               goto out;
-
-       ip_vs_nat_icmp_v6(skb, pp, cp, 1);
-
-       /* do the statistics and put it back */
-       ip_vs_out_stats(cp, skb);
-
-       skb->ipvs_property = 1;
-       verdict = NF_ACCEPT;
-
-out:
-       __ip_vs_conn_put(cp);
-
-       return verdict;
+       snet.in6 = iph->saddr;
+       return handle_response_icmp(AF_INET6, skb, &snet, cih->nexthdr, cp,
+                                   pp, offset, sizeof(struct ipv6hdr));
 }
 #endif
 
@@ -1055,7 +1033,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb,
                                                          ICMP_DEST_UNREACH,
                                                          ICMP_PORT_UNREACH, 0);
                                        return NF_DROP;
-                               }
+                       }
                        }
                }
                IP_VS_DBG_PKT(12, pp, skb, 0,
@@ -1083,6 +1061,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
        struct ip_vs_conn *cp;
        struct ip_vs_protocol *pp;
        unsigned int offset, ihl, verdict;
+       union nf_inet_addr snet;
 
        *related = 1;
 
@@ -1142,9 +1121,12 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
        if (!cp) {
                /* The packet could also belong to a local client */
                cp = pp->conn_out_get(AF_INET, skb, pp, &ciph, offset, 1);
-               if (cp)
-                       return handle_response_icmp(skb, iph, cih, cp, pp,
+               if (cp) {
+                       snet.ip = iph->saddr;
+                       return handle_response_icmp(AF_INET, skb, &snet,
+                                                   cih->protocol, cp, pp,
                                                    offset, ihl);
+               }
                return NF_ACCEPT;
        }
 
@@ -1183,6 +1165,7 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum)
        struct ip_vs_conn *cp;
        struct ip_vs_protocol *pp;
        unsigned int offset, verdict;
+       union nf_inet_addr snet;
 
        *related = 1;
 
@@ -1240,8 +1223,18 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum)
        ip_vs_fill_iphdr(AF_INET6, cih, &ciph);
        /* The embedded headers contain source and dest in reverse order */
        cp = pp->conn_in_get(AF_INET6, skb, pp, &ciph, offset, 1);
-       if (!cp)
+       if (!cp) {
+               /* The packet could also belong to a local client */
+               cp = pp->conn_out_get(AF_INET6, skb, pp, &ciph, offset, 1);
+               if (cp) {
+                       snet.in6 = iph->saddr;
+                       return handle_response_icmp(AF_INET6, skb, &snet,
+                                                   cih->nexthdr,
+                                                   cp, pp, offset,
+                                                   sizeof(struct ipv6hdr));
+               }
                return NF_ACCEPT;
+       }
 
        verdict = NF_DROP;
 
@@ -1281,9 +1274,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb,
         *      Big tappo: only PACKET_HOST, including loopback for local client
         *      Don't handle local packets on IPv6 for now
         */
-       if (unlikely(skb->pkt_type != PACKET_HOST ||
-                    (af == AF_INET6 || (skb->dev->flags & IFF_LOOPBACK ||
-                                        skb->sk)))) {
+       if (unlikely(skb->pkt_type != PACKET_HOST)) {
                IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s ignored\n",
                              skb->pkt_type,
                              iph.protocol,