[NETFILTER]: nf_nat: use HW checksumming when possible
authorPatrick McHardy <kaber@trash.net>
Fri, 13 Apr 2007 05:15:50 +0000 (22:15 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Thu, 26 Apr 2007 05:28:59 +0000 (22:28 -0700)
When mangling packets forwarded to a HW checksumming capable device,
offload recalculation of the checksum instead of doing it in software.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/netfilter/nf_nat_helper.c

index 8a40fbe842b7cb197d0ddc5ab852e0f6b9176f5b..15b6e5ce3a04127c4331e89053aad0f85081126e 100644 (file)
@@ -153,6 +153,7 @@ nf_nat_mangle_tcp_packet(struct sk_buff **pskb,
                         const char *rep_buffer,
                         unsigned int rep_len)
 {
+       struct rtable *rt = (struct rtable *)(*pskb)->dst;
        struct iphdr *iph;
        struct tcphdr *tcph;
        int oldlen, datalen;
@@ -176,11 +177,22 @@ nf_nat_mangle_tcp_packet(struct sk_buff **pskb,
 
        datalen = (*pskb)->len - iph->ihl*4;
        if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
-               tcph->check = 0;
-               tcph->check = tcp_v4_check(datalen,
-                                          iph->saddr, iph->daddr,
-                                          csum_partial((char *)tcph,
-                                                       datalen, 0));
+               if (!(rt->rt_flags & RTCF_LOCAL) &&
+                   (*pskb)->dev->features & NETIF_F_ALL_CSUM) {
+                       (*pskb)->ip_summed = CHECKSUM_PARTIAL;
+                       (*pskb)->csum_start = skb_headroom(*pskb) +
+                                             skb_network_offset(*pskb) +
+                                             iph->ihl * 4;
+                       (*pskb)->csum_offset = offsetof(struct tcphdr, check);
+                       tcph->check = ~tcp_v4_check(datalen,
+                                                   iph->saddr, iph->daddr, 0);
+               } else {
+                       tcph->check = 0;
+                       tcph->check = tcp_v4_check(datalen,
+                                                  iph->saddr, iph->daddr,
+                                                  csum_partial((char *)tcph,
+                                                               datalen, 0));
+               }
        } else
                nf_proto_csum_replace2(&tcph->check, *pskb,
                                       htons(oldlen), htons(datalen), 1);
@@ -217,6 +229,7 @@ nf_nat_mangle_udp_packet(struct sk_buff **pskb,
                         const char *rep_buffer,
                         unsigned int rep_len)
 {
+       struct rtable *rt = (struct rtable *)(*pskb)->dst;
        struct iphdr *iph;
        struct udphdr *udph;
        int datalen, oldlen;
@@ -251,13 +264,25 @@ nf_nat_mangle_udp_packet(struct sk_buff **pskb,
                return 1;
 
        if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
-               udph->check = 0;
-               udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
-                                               datalen, IPPROTO_UDP,
-                                               csum_partial((char *)udph,
-                                                            datalen, 0));
-               if (!udph->check)
-                       udph->check = CSUM_MANGLED_0;
+               if (!(rt->rt_flags & RTCF_LOCAL) &&
+                   (*pskb)->dev->features & NETIF_F_ALL_CSUM) {
+                       (*pskb)->ip_summed = CHECKSUM_PARTIAL;
+                       (*pskb)->csum_start = skb_headroom(*pskb) +
+                                             skb_network_offset(*pskb) +
+                                             iph->ihl * 4;
+                       (*pskb)->csum_offset = offsetof(struct udphdr, check);
+                       udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+                                                        datalen, IPPROTO_UDP,
+                                                        0);
+               } else {
+                       udph->check = 0;
+                       udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
+                                                       datalen, IPPROTO_UDP,
+                                                       csum_partial((char *)udph,
+                                                                    datalen, 0));
+                       if (!udph->check)
+                               udph->check = CSUM_MANGLED_0;
+               }
        } else
                nf_proto_csum_replace2(&udph->check, *pskb,
                                       htons(oldlen), htons(datalen), 1);