net: ipvs: sctp: do not recalc sctp csum when ports didn't change
[linux.git] / net / netfilter / ipvs / ip_vs_proto_sctp.c
index 9ca7aa033284c2e48caaad0030905fdabd2280db..2f7ea7564044ccd0d8600a2b6688cb6603645588 100644 (file)
@@ -81,6 +81,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
 {
        sctp_sctphdr_t *sctph;
        unsigned int sctphoff = iph->len;
+       bool payload_csum = false;
 
 #ifdef CONFIG_IP_VS_IPV6
        if (cp->af == AF_INET6 && iph->fragoffs)
@@ -92,19 +93,31 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
                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;
 
                /* Call application helper if needed */
-               if (!ip_vs_app_pkt_out(cp, skb))
+               ret = ip_vs_app_pkt_out(cp, skb);
+               if (ret == 0)
                        return 0;
+               /* ret=2: csum update is needed after payload mangling */
+               if (ret == 2)
+                       payload_csum = true;
        }
 
        sctph = (void *) skb_network_header(skb) + sctphoff;
-       sctph->source = cp->vport;
 
-       sctp_nat_csum(skb, sctph, sctphoff);
+       /* Only update csum if we really have to */
+       if (sctph->source != cp->vport || payload_csum ||
+           skb->ip_summed == CHECKSUM_PARTIAL) {
+               sctph->source = cp->vport;
+               sctp_nat_csum(skb, sctph, sctphoff);
+       } else {
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       }
 
        return 1;
 }
@@ -115,6 +128,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
 {
        sctp_sctphdr_t *sctph;
        unsigned int sctphoff = iph->len;
+       bool payload_csum = false;
 
 #ifdef CONFIG_IP_VS_IPV6
        if (cp->af == AF_INET6 && iph->fragoffs)
@@ -126,19 +140,32 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
                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;
 
                /* Call application helper if needed */
-               if (!ip_vs_app_pkt_in(cp, skb))
+               ret = ip_vs_app_pkt_in(cp, skb);
+               if (ret == 0)
                        return 0;
+               /* ret=2: csum update is needed after payload mangling */
+               if (ret == 2)
+                       payload_csum = true;
        }
 
        sctph = (void *) skb_network_header(skb) + sctphoff;
-       sctph->dest = cp->dport;
 
-       sctp_nat_csum(skb, sctph, sctphoff);
+       /* Only update csum if we really have to */
+       if (sctph->dest != cp->dport || payload_csum ||
+           (skb->ip_summed == CHECKSUM_PARTIAL &&
+            !(skb_dst(skb)->dev->features & NETIF_F_SCTP_CSUM))) {
+               sctph->dest = cp->dport;
+               sctp_nat_csum(skb, sctph, sctphoff);
+       } else if (skb->ip_summed != CHECKSUM_PARTIAL) {
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       }
 
        return 1;
 }