diff options
author | Patrick McHardy <kaber@trash.net> | 2007-04-12 22:15:50 -0700 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-04-25 22:28:59 -0700 |
commit | fe6092ea0019cbba5263a915c9ce9f2bf383209e (patch) | |
tree | 0da8554466720878ce6fec639eebe7ea2e7ec96e /net | |
parent | c15bf6e699f4c366f2d1e19ac5d7add21c6b5a19 (diff) | |
download | linux-fe6092ea0019cbba5263a915c9ce9f2bf383209e.tar.bz2 |
[NETFILTER]: nf_nat: use HW checksumming when possible
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/netfilter/nf_nat_helper.c | 49 |
1 files changed, 37 insertions, 12 deletions
diff --git a/net/ipv4/netfilter/nf_nat_helper.c b/net/ipv4/netfilter/nf_nat_helper.c index 8a40fbe842b7..15b6e5ce3a04 100644 --- a/net/ipv4/netfilter/nf_nat_helper.c +++ b/net/ipv4/netfilter/nf_nat_helper.c @@ -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); |