[BRIDGE] netfilter: vlan + hw checksum = bug?
Stephen Hemminger [Fri, 6 Jan 2006 21:13:29 +0000 (13:13 -0800)]
It looks like the bridge netfilter code does not correctly update
the hardware checksum after popping off the VLAN header.

This is by inspection, I have *not* tested this.
To test you would need to set up a filtering bridge with vlans
and a device the does hardware receive checksum (skge, or sungem)

Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

net/bridge/br_netfilter.c

index 223f827..7cac3fb 100644 (file)
@@ -394,8 +394,9 @@ inhdr_error:
  * target in particular.  Save the original destination IP
  * address to be able to detect DNAT afterwards. */
 static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
-   const struct net_device *in, const struct net_device *out,
-   int (*okfn)(struct sk_buff *))
+                                     const struct net_device *in,
+                                     const struct net_device *out,
+                                     int (*okfn)(struct sk_buff *))
 {
        struct iphdr *iph;
        __u32 len;
@@ -412,8 +413,10 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
                        goto out;
 
                if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+                       u8 *vhdr = skb->data;
                        skb_pull(skb, VLAN_HLEN);
-                       (skb)->nh.raw += VLAN_HLEN;
+                       skb_postpull_rcsum(skb, vhdr, VLAN_HLEN);
+                       skb->nh.raw += VLAN_HLEN;
                }
                return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn);
        }
@@ -429,8 +432,10 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
                goto out;
 
        if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+               u8 *vhdr = skb->data;
                skb_pull(skb, VLAN_HLEN);
-               (skb)->nh.raw += VLAN_HLEN;
+               skb_postpull_rcsum(skb, vhdr, VLAN_HLEN);
+               skb->nh.raw += VLAN_HLEN;
        }
 
        if (!pskb_may_pull(skb, sizeof(struct iphdr)))