LVS
lvs-devel
Google
 
Web LinuxVirtualServer.org

[PATCH 10/26] IPVS: Add IPv6 handler functions to UDP protocol handler.

To: lvs-devel@xxxxxxxxxxxxxxx, netdev@xxxxxxxxxxxxxxx
Subject: [PATCH 10/26] IPVS: Add IPv6 handler functions to UDP protocol handler.
Cc: horms@xxxxxxxxxxxx, davem@xxxxxxxxxxxxx, vbusam@xxxxxxxxxx, "Julius R. Volz" <juliusv@xxxxxxxxxx>
From: "Julius R. Volz" <juliusv@xxxxxxxxxx>
Date: Wed, 11 Jun 2008 19:11:53 +0200
Define new IPv6-specific handler functions in UDP protocol handler. Set new
function pointers in ip_vs_protocol struct to point to these functions.

Signed-off-by: Julius R. Volz <juliusv@xxxxxxxxxx>

 1 files changed, 264 insertions(+), 1 deletions(-)

diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c 
b/net/netfilter/ipvs/ip_vs_proto_udp.c
index 76e97ef..ef0d921 100644
--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
@@ -49,6 +49,31 @@ udp_conn_in_get(const struct sk_buff *skb, struct 
ip_vs_protocol *pp,
        return cp;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static struct ip_vs_conn *
+udp_conn_in_get_v6(const struct sk_buff *skb, struct ip_vs_protocol *pp,
+                  const struct ipv6hdr *iph, unsigned int proto_off, int 
inverse)
+{
+       struct ip_vs_conn *cp;
+       __be16 _ports[2], *pptr;
+
+       pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports);
+       if (pptr == NULL)
+               return NULL;
+
+       if (likely(!inverse)) {
+               cp = ip_vs_conn_in_get_v6(iph->nexthdr,
+                                         &iph->saddr, pptr[0],
+                                         &iph->daddr, pptr[1]);
+       } else {
+               cp = ip_vs_conn_in_get_v6(iph->nexthdr,
+                                         &iph->daddr, pptr[1],
+                                         &iph->saddr, pptr[0]);
+       }
+
+       return cp;
+}
+#endif
 
 static struct ip_vs_conn *
 udp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
@@ -75,6 +100,33 @@ udp_conn_out_get(const struct sk_buff *skb, struct 
ip_vs_protocol *pp,
        return cp;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static struct ip_vs_conn *
+udp_conn_out_get_v6(const struct sk_buff *skb, struct ip_vs_protocol *pp,
+                   const struct ipv6hdr *iph, unsigned int proto_off, int 
inverse)
+{
+       struct ip_vs_conn *cp;
+       __be16 _ports[2], *pptr;
+
+       pptr = skb_header_pointer(skb, sizeof(struct ipv6hdr),
+                                 sizeof(_ports), _ports);
+       if (pptr == NULL)
+               return NULL;
+
+       if (likely(!inverse)) {
+               cp = ip_vs_conn_out_get_v6(iph->nexthdr,
+                                          &iph->saddr, pptr[0],
+                                          &iph->daddr, pptr[1]);
+       } else {
+               cp = ip_vs_conn_out_get_v6(iph->nexthdr,
+                                          &iph->daddr, pptr[1],
+                                          &iph->saddr, pptr[0]);
+       }
+
+       return cp;
+}
+#endif
+
 
 static int
 udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp,
@@ -116,6 +168,48 @@ udp_conn_schedule(struct sk_buff *skb, struct 
ip_vs_protocol *pp,
        return 1;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static int
+udp_conn_schedule_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                    int *verdict, struct ip_vs_conn **cpp)
+{
+       struct ip_vs_service *svc;
+       struct udphdr _udph, *uh;
+
+       uh = skb_header_pointer(skb, sizeof(struct ipv6hdr),
+                               sizeof(_udph), &_udph);
+       if (uh == NULL) {
+               *verdict = NF_DROP;
+               return 0;
+       }
+
+       if ((svc = ip_vs_service_get_v6(skb->mark, ipv6_hdr(skb)->nexthdr,
+                                       &ipv6_hdr(skb)->daddr, uh->dest))) {
+               if (ip_vs_todrop()) {
+                       /*
+                        * It seems that we are very loaded.
+                        * We have to drop this packet :(
+                        */
+                       ip_vs_service_put(svc);
+                       *verdict = NF_DROP;
+                       return 0;
+               }
+
+               /*
+                * Let the virtual server select a real server for the
+                * incoming connection, and create a connection entry.
+                */
+               *cpp = ip_vs_schedule_v6(svc, skb);
+               if (!*cpp) {
+                       *verdict = ip_vs_leave_v6(svc, skb, pp);
+                       return 0;
+               }
+               ip_vs_service_put(svc);
+       }
+       return 1;
+}
+#endif
+
 
 static inline void
 udp_fast_csum_update(struct udphdr *uhdr, __be32 oldip, __be32 newip,
@@ -129,6 +223,21 @@ udp_fast_csum_update(struct udphdr *uhdr, __be32 oldip, 
__be32 newip,
                uhdr->check = CSUM_MANGLED_0;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static inline void
+udp_fast_csum_update_v6(struct udphdr *uhdr, const struct in6_addr *oldip,
+                       const struct in6_addr *newip, __be16 oldport,
+                       __be16 newport)
+{
+       uhdr->check =
+               csum_fold(ip_vs_check_diff16(oldip->s6_addr32, newip->s6_addr32,
+                                ip_vs_check_diff2(oldport, newport,
+                                       ~csum_unfold(uhdr->check))));
+       if (!uhdr->check)
+               uhdr->check = CSUM_MANGLED_0;
+}
+#endif
+
 static int
 udp_snat_handler(struct sk_buff *skb,
                 struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
@@ -180,6 +289,59 @@ udp_snat_handler(struct sk_buff *skb,
        return 1;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static int
+udp_snat_handler_v6(struct sk_buff *skb,
+                   struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+{
+       struct udphdr *udph;
+       const unsigned int udphoff = sizeof(struct ipv6hdr);
+
+       /* csum_check requires unshared skb */
+       if (!skb_make_writable(skb, udphoff+sizeof(*udph)))
+               return 0;
+
+       if (unlikely(cp->app != NULL)) {
+               /* Some checks before mangling */
+               if (pp->csum_check_v6 && !pp->csum_check_v6(skb, pp))
+                       return 0;
+
+               /*
+                *      Call application helper if needed
+                */
+               if (!ip_vs_app_pkt_out(cp, skb))
+                       return 0;
+       }
+
+       udph = (void *)ipv6_hdr(skb) + udphoff;
+       udph->source = cp->vport;
+
+       /*
+        *      Adjust UDP checksums
+        */
+       if (!cp->app && (udph->check != 0)) {
+               /* Only port and addr are changed, do fast csum update */
+               udp_fast_csum_update_v6(udph, &cp->daddr.v6, &cp->vaddr.v6,
+                                       cp->dport, cp->vport);
+               if (skb->ip_summed == CHECKSUM_COMPLETE)
+                       skb->ip_summed = CHECKSUM_NONE;
+       } else {
+               /* full checksum calculation */
+               udph->check = 0;
+               skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0);
+               udph->check = csum_ipv6_magic(&cp->vaddr.v6, &cp->caddr.v6,
+                                             skb->len - udphoff,
+                                             cp->protocol, skb->csum);
+               if (udph->check == 0)
+                       udph->check = CSUM_MANGLED_0;
+               IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n",
+                         pp->name, udph->check,
+                         (char*)&(udph->check) - (char*)udph);
+       }
+       return 1;
+}
+#endif
+
 
 static int
 udp_dnat_handler(struct sk_buff *skb,
@@ -231,6 +393,58 @@ udp_dnat_handler(struct sk_buff *skb,
        return 1;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static int
+udp_dnat_handler_v6(struct sk_buff *skb,
+                   struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+{
+       struct udphdr *udph;
+       unsigned int udphoff = sizeof(struct ipv6hdr);
+
+       /* csum_check requires unshared skb */
+       if (!skb_make_writable(skb, udphoff+sizeof(*udph)))
+               return 0;
+
+       if (unlikely(cp->app != NULL)) {
+               /* Some checks before mangling */
+               if (pp->csum_check_v6 && !pp->csum_check_v6(skb, pp))
+                       return 0;
+
+               /*
+                *      Attempt ip_vs_app call.
+                *      It will fix ip_vs_conn
+                */
+               if (!ip_vs_app_pkt_in(cp, skb))
+                       return 0;
+       }
+
+       udph = (void *)ipv6_hdr(skb) + udphoff;
+       udph->dest = cp->dport;
+
+       /*
+        *      Adjust UDP checksums
+        */
+       if (!cp->app && (udph->check != 0)) {
+               /* Only port and addr are changed, do fast csum update */
+               udp_fast_csum_update_v6(udph, &cp->vaddr.v6, &cp->daddr.v6,
+                                       cp->vport, cp->dport);
+               if (skb->ip_summed == CHECKSUM_COMPLETE)
+                       skb->ip_summed = CHECKSUM_NONE;
+       } else {
+               /* full checksum calculation */
+               udph->check = 0;
+               skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0);
+               udph->check = csum_ipv6_magic(&cp->caddr.v6, &cp->daddr.v6,
+                                             skb->len - udphoff,
+                                             cp->protocol, skb->csum);
+               if (udph->check == 0)
+                       udph->check = CSUM_MANGLED_0;
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       }
+       return 1;
+}
+#endif
+
 
 static int
 udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
@@ -266,6 +480,42 @@ udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol 
*pp)
        return 1;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static int
+udp_csum_check_v6(struct sk_buff *skb, struct ip_vs_protocol *pp)
+{
+       struct udphdr _udph, *uh;
+       const unsigned int udphoff = sizeof(struct ipv6hdr);
+
+       uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
+       if (uh == NULL)
+               return 0;
+
+       if (uh->check != 0) {
+               switch (skb->ip_summed) {
+               case CHECKSUM_NONE:
+                       skb->csum = skb_checksum(skb, udphoff,
+                                                skb->len - udphoff, 0);
+               case CHECKSUM_COMPLETE:
+                       if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+                                           &ipv6_hdr(skb)->daddr,
+                                           skb->len - udphoff,
+                                           ipv6_hdr(skb)->nexthdr,
+                                           skb->csum)) {
+                               IP_VS_DBG_RL_PKT(0, pp, skb, 0,
+                                                "Failed checksum for");
+                               return 0;
+                       }
+                       break;
+               default:
+                       /* No need to checksum. */
+                       break;
+               }
+       }
+       return 1;
+}
+#endif
+
 
 /*
  *     Note: the caller guarantees that only one of register_app,
@@ -413,7 +663,6 @@ static void udp_exit(struct ip_vs_protocol *pp)
 {
 }
 
-
 struct ip_vs_protocol ip_vs_protocol_udp = {
        .name =                 "UDP",
        .protocol =             IPPROTO_UDP,
@@ -422,11 +671,25 @@ struct ip_vs_protocol ip_vs_protocol_udp = {
        .init =                 udp_init,
        .exit =                 udp_exit,
        .conn_schedule =        udp_conn_schedule,
+#ifdef CONFIG_IP_VS_IPV6
+       .conn_schedule_v6 =     udp_conn_schedule_v6,
+#endif
        .conn_in_get =          udp_conn_in_get,
        .conn_out_get =         udp_conn_out_get,
+#ifdef CONFIG_IP_VS_IPV6
+       .conn_in_get_v6 =       udp_conn_in_get_v6,
+       .conn_out_get_v6 =      udp_conn_out_get_v6,
+#endif
        .snat_handler =         udp_snat_handler,
        .dnat_handler =         udp_dnat_handler,
+#ifdef CONFIG_IP_VS_IPV6
+       .snat_handler_v6 =      udp_snat_handler_v6,
+       .dnat_handler_v6 =      udp_dnat_handler_v6,
+#endif
        .csum_check =           udp_csum_check,
+#ifdef CONFIG_IP_VS_IPV6
+       .csum_check_v6 =        udp_csum_check_v6,
+#endif
        .state_transition =     udp_state_transition,
        .state_name =           udp_state_name,
        .register_app =         udp_register_app,
-- 
1.5.3.6

--
To unsubscribe from this list: send the line "unsubscribe lvs-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

<Prev in Thread] Current Thread [Next in Thread>