summaryrefslogtreecommitdiff
path: root/net/core/dev.c
diff options
context:
space:
mode:
authorJames Morris <jmorris@redhat.com>2004-06-03 08:03:01 -0700
committerJames Morris <jmorris@redhat.com>2004-06-03 08:03:01 -0700
commitb0f171707fba2fc73bc104a6a79964c4f0cfc46f (patch)
treea01d8171b88c33e5306b357c6e0b37707ebb3c76 /net/core/dev.c
parent5a1c7700d664d88062c69842efd693f5a1aa03af (diff)
[NETFILTER]: Fix checksum bug for multicast/broadcast packets on postrouting hook.
In a nutshell, skb checksum mangling has been removed from nf_hook_slow() and pushed up to whatever really needs to do it. Namely: NAT, ip_fw_compat, ipt_TCPMSS, IPSec transforms. skb_checksum_help() has been changed to perform an skb_copy() if needed (e.g. the original problem case where bcast/mcast was cloning packets for transmission over loopback, changing ip_summed). Because of the above, the output path has been modified to take into account the fact that an skb may need to be changed in some places. There are some minor changes in the routing code to take care of the now different input and output function prototypes. The ipv6 fragmentation code has been modified to detect a changed skb. The rest of the patch (probably the bulk of it) is simply the result of changing to double skb pointers. I've tested this with ipv4, ipv6, ipsec (including xfrm bundles), NAT and the original DHCP test case. Everything seems to be working ok. Signed-off-by: James Morris <jmorris@redhat.com> Signed-off-by: David S. Miller <davem@redhat.com>
Diffstat (limited to 'net/core/dev.c')
-rw-r--r--net/core/dev.c49
1 files changed, 33 insertions, 16 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index dfd505c1c511..fb20e1047f27 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1180,28 +1180,46 @@ void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
rcu_read_unlock();
}
-/* Calculate csum in the case, when packet is misrouted.
- * If it failed by some reason, ignore and send skb with wrong
- * checksum.
+/*
+ * Invalidate hardware checksum when packet is to be mangled, and
+ * complete checksum manually on outgoing path.
*/
-struct sk_buff *skb_checksum_help(struct sk_buff *skb)
+int skb_checksum_help(struct sk_buff **pskb, int inward)
{
unsigned int csum;
- int offset = skb->h.raw - skb->data;
+ int ret = 0, offset = (*pskb)->h.raw - (*pskb)->data;
+
+ if (inward) {
+ (*pskb)->ip_summed = CHECKSUM_NONE;
+ goto out;
+ }
- if (offset > (int)skb->len)
+ if (skb_shared(*pskb) || skb_cloned(*pskb)) {
+ struct sk_buff *newskb = skb_copy(*pskb, GFP_ATOMIC);
+ if (!newskb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if ((*pskb)->sk)
+ skb_set_owner_w(newskb, (*pskb)->sk);
+ kfree_skb(*pskb);
+ *pskb = newskb;
+ }
+
+ if (offset > (int)(*pskb)->len)
BUG();
- csum = skb_checksum(skb, offset, skb->len-offset, 0);
+ csum = skb_checksum(*pskb, offset, (*pskb)->len-offset, 0);
- offset = skb->tail - skb->h.raw;
+ offset = (*pskb)->tail - (*pskb)->h.raw;
if (offset <= 0)
BUG();
- if (skb->csum + 2 > offset)
+ if ((*pskb)->csum + 2 > offset)
BUG();
- *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum);
- skb->ip_summed = CHECKSUM_NONE;
- return skb;
+ *(u16*)((*pskb)->h.raw + (*pskb)->csum) = csum_fold(csum);
+ (*pskb)->ip_summed = CHECKSUM_NONE;
+out:
+ return ret;
}
#ifdef CONFIG_HIGHMEM
@@ -1326,10 +1344,9 @@ int dev_queue_xmit(struct sk_buff *skb)
if (skb->ip_summed == CHECKSUM_HW &&
(!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) &&
(!(dev->features & NETIF_F_IP_CSUM) ||
- skb->protocol != htons(ETH_P_IP)))) {
- if ((skb = skb_checksum_help(skb)) == NULL)
- goto out;
- }
+ skb->protocol != htons(ETH_P_IP))))
+ if (skb_checksum_help(&skb, 0))
+ goto out_kfree_skb;
/* Grab device queue */
spin_lock_bh(&dev->queue_lock);