]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/netfilter/ipvs/ip_vs_proto_udp.c
Merge branch 'master' into csb1725
[mv-sheeva.git] / net / netfilter / ipvs / ip_vs_proto_udp.c
index 8553231b5d412ca557f8699ee998e05351152213..9d106a06bb0a46376252b32f2d30882d921b8b16 100644 (file)
@@ -46,6 +46,8 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
        svc = ip_vs_service_get(af, skb->mark, iph.protocol,
                                &iph.daddr, uh->dest);
        if (svc) {
+               int ignored;
+
                if (ip_vs_todrop()) {
                        /*
                         * It seems that we are very loaded.
@@ -60,8 +62,8 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
                 * Let the virtual server select a real server for the
                 * incoming connection, and create a connection entry.
                 */
-               *cpp = ip_vs_schedule(svc, skb);
-               if (!*cpp) {
+               *cpp = ip_vs_schedule(svc, skb, pp, &ignored);
+               if (!*cpp && !ignored) {
                        *verdict = ip_vs_leave(svc, skb, pp);
                        return 0;
                }
@@ -102,15 +104,15 @@ udp_partial_csum_update(int af, struct udphdr *uhdr,
 #ifdef CONFIG_IP_VS_IPV6
        if (af == AF_INET6)
                uhdr->check =
-                       csum_fold(ip_vs_check_diff16(oldip->ip6, newip->ip6,
+                       ~csum_fold(ip_vs_check_diff16(oldip->ip6, newip->ip6,
                                         ip_vs_check_diff2(oldlen, newlen,
-                                               ~csum_unfold(uhdr->check))));
+                                               csum_unfold(uhdr->check))));
        else
 #endif
        uhdr->check =
-               csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip,
+               ~csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip,
                                ip_vs_check_diff2(oldlen, newlen,
-                                               ~csum_unfold(uhdr->check))));
+                                               csum_unfold(uhdr->check))));
 }
 
 
@@ -121,6 +123,7 @@ udp_snat_handler(struct sk_buff *skb,
        struct udphdr *udph;
        unsigned int udphoff;
        int oldlen;
+       int payload_csum = 0;
 
 #ifdef CONFIG_IP_VS_IPV6
        if (cp->af == AF_INET6)
@@ -135,6 +138,8 @@ udp_snat_handler(struct sk_buff *skb,
                return 0;
 
        if (unlikely(cp->app != NULL)) {
+               int ret;
+
                /* Some checks before mangling */
                if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
                        return 0;
@@ -142,8 +147,13 @@ udp_snat_handler(struct sk_buff *skb,
                /*
                 *      Call application helper if needed
                 */
-               if (!ip_vs_app_pkt_out(cp, skb))
+               if (!(ret = ip_vs_app_pkt_out(cp, skb)))
                        return 0;
+               /* ret=2: csum update is needed after payload mangling */
+               if (ret == 1)
+                       oldlen = skb->len - udphoff;
+               else
+                       payload_csum = 1;
        }
 
        udph = (void *)skb_network_header(skb) + udphoff;
@@ -156,12 +166,13 @@ udp_snat_handler(struct sk_buff *skb,
                udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr,
                                        htons(oldlen),
                                        htons(skb->len - udphoff));
-       } else if (!cp->app && (udph->check != 0)) {
+       } else if (!payload_csum && (udph->check != 0)) {
                /* Only port and addr are changed, do fast csum update */
                udp_fast_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr,
                                     cp->dport, cp->vport);
                if (skb->ip_summed == CHECKSUM_COMPLETE)
-                       skb->ip_summed = CHECKSUM_NONE;
+                       skb->ip_summed = (cp->app && pp->csum_check) ?
+                                        CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
        } else {
                /* full checksum calculation */
                udph->check = 0;
@@ -181,6 +192,7 @@ udp_snat_handler(struct sk_buff *skb,
                                                        skb->csum);
                if (udph->check == 0)
                        udph->check = CSUM_MANGLED_0;
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
                IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n",
                          pp->name, udph->check,
                          (char*)&(udph->check) - (char*)udph);
@@ -196,6 +208,7 @@ udp_dnat_handler(struct sk_buff *skb,
        struct udphdr *udph;
        unsigned int udphoff;
        int oldlen;
+       int payload_csum = 0;
 
 #ifdef CONFIG_IP_VS_IPV6
        if (cp->af == AF_INET6)
@@ -210,6 +223,8 @@ udp_dnat_handler(struct sk_buff *skb,
                return 0;
 
        if (unlikely(cp->app != NULL)) {
+               int ret;
+
                /* Some checks before mangling */
                if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
                        return 0;
@@ -218,8 +233,13 @@ udp_dnat_handler(struct sk_buff *skb,
                 *      Attempt ip_vs_app call.
                 *      It will fix ip_vs_conn
                 */
-               if (!ip_vs_app_pkt_in(cp, skb))
+               if (!(ret = ip_vs_app_pkt_in(cp, skb)))
                        return 0;
+               /* ret=2: csum update is needed after payload mangling */
+               if (ret == 1)
+                       oldlen = skb->len - udphoff;
+               else
+                       payload_csum = 1;
        }
 
        udph = (void *)skb_network_header(skb) + udphoff;
@@ -229,15 +249,16 @@ udp_dnat_handler(struct sk_buff *skb,
         *      Adjust UDP checksums
         */
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
-               udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr,
+               udp_partial_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr,
                                        htons(oldlen),
                                        htons(skb->len - udphoff));
-       } else if (!cp->app && (udph->check != 0)) {
+       } else if (!payload_csum && (udph->check != 0)) {
                /* Only port and addr are changed, do fast csum update */
                udp_fast_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr,
                                     cp->vport, cp->dport);
                if (skb->ip_summed == CHECKSUM_COMPLETE)
-                       skb->ip_summed = CHECKSUM_NONE;
+                       skb->ip_summed = (cp->app && pp->csum_check) ?
+                                        CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
        } else {
                /* full checksum calculation */
                udph->check = 0;
@@ -293,7 +314,7 @@ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
                                                    skb->len - udphoff,
                                                    ipv6_hdr(skb)->nexthdr,
                                                    skb->csum)) {
-                                       IP_VS_DBG_RL_PKT(0, pp, skb, 0,
+                                       IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
                                                         "Failed checksum for");
                                        return 0;
                                }
@@ -304,7 +325,7 @@ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
                                                      skb->len - udphoff,
                                                      ip_hdr(skb)->protocol,
                                                      skb->csum)) {
-                                       IP_VS_DBG_RL_PKT(0, pp, skb, 0,
+                                       IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
                                                         "Failed checksum for");
                                        return 0;
                                }