]> git.hungrycats.org Git - linux/commitdiff
[NETFILTER]: Fix checksum bug for multicast/broadcast packets on postrouting hook.
authorJames Morris <jmorris@redhat.com>
Thu, 3 Jun 2004 15:03:01 +0000 (08:03 -0700)
committerJames Morris <jmorris@redhat.com>
Thu, 3 Jun 2004 15:03:01 +0000 (08:03 -0700)
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>
26 files changed:
include/linux/netdevice.h
include/net/dst.h
include/net/ip.h
include/net/ip6_route.h
include/net/ipv6.h
include/net/xfrm.h
net/bridge/br_netfilter.c
net/core/dev.c
net/core/dst.c
net/core/netfilter.c
net/decnet/dn_route.c
net/ipv4/ah4.c
net/ipv4/esp4.c
net/ipv4/ip_output.c
net/ipv4/ipcomp.c
net/ipv4/netfilter/ip_fw_compat.c
net/ipv4/netfilter/ip_nat_standalone.c
net/ipv4/netfilter/ipt_ECN.c
net/ipv4/netfilter/ipt_TCPMSS.c
net/ipv4/route.c
net/ipv4/xfrm4_tunnel.c
net/ipv6/ah6.c
net/ipv6/esp6.c
net/ipv6/ip6_output.c
net/ipv6/ipcomp6.c
net/ipv6/route.c

index d2c25ff41010336cfbf3aafe25179c533593c9da..a55f97a3403583da16d35cabebba0180c78be63c 100644 (file)
@@ -943,7 +943,7 @@ extern int          weight_p;
 extern unsigned long   netdev_fc_xoff;
 extern atomic_t netdev_dropping;
 extern int             netdev_set_master(struct net_device *dev, struct net_device *master);
-extern struct sk_buff * skb_checksum_help(struct sk_buff *skb);
+extern int skb_checksum_help(struct sk_buff **pskb, int inward);
 #ifdef CONFIG_NET_FASTROUTE
 extern int             netdev_fastroute;
 extern int             netdev_fastroute_obstacles;
index 42dd8f511cefc8d3a62dc1c09a03a3d5275fa1e2..a7d83f545c28eae9a491568553bf726498f79c7d 100644 (file)
@@ -67,7 +67,7 @@ struct dst_entry
        struct xfrm_state       *xfrm;
 
        int                     (*input)(struct sk_buff*);
-       int                     (*output)(struct sk_buff*);
+       int                     (*output)(struct sk_buff**);
 
 #ifdef CONFIG_NET_CLS_ROUTE
        __u32                   tclassid;
@@ -219,7 +219,7 @@ static inline int dst_output(struct sk_buff *skb)
        int err;
 
        for (;;) {
-               err = skb->dst->output(skb);
+               err = skb->dst->output(&skb);
 
                if (likely(err == 0))
                        return err;
index 94ea1eda3b3dc234e94cf9f56a305c415b9d0d9d..5a683ccd4cb03e949ed33bad0efb2f6c5d285aab 100644 (file)
@@ -92,8 +92,8 @@ extern int            ip_rcv(struct sk_buff *skb, struct net_device *dev,
                               struct packet_type *pt);
 extern int             ip_local_deliver(struct sk_buff *skb);
 extern int             ip_mr_input(struct sk_buff *skb);
-extern int             ip_output(struct sk_buff *skb);
-extern int             ip_mc_output(struct sk_buff *skb);
+extern int             ip_output(struct sk_buff **pskb);
+extern int             ip_mc_output(struct sk_buff **pskb);
 extern int             ip_fragment(struct sk_buff *skb, int (*out)(struct sk_buff*));
 extern int             ip_do_nat(struct sk_buff *skb);
 extern void            ip_send_check(struct iphdr *ip);
index ea2781128829b3cf9e7a6c3fd61f42f788fe8596..291149080a043634f26222a7b133601dd38eab58 100644 (file)
@@ -65,7 +65,7 @@ extern struct rt6_info                *rt6_lookup(struct in6_addr *daddr,
 extern struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
                                         struct neighbour *neigh,
                                         struct in6_addr *addr,
-                                        int (*output)(struct sk_buff *));
+                                        int (*output)(struct sk_buff **));
 extern int ndisc_dst_gc(int *more);
 extern void fib6_force_start_gc(void);
 
index 06b350cef868fa9992ebfafa3974a521a0986e08..7b000ee2bef19fe25fc35fac07be2a3e54a75ae2 100644 (file)
@@ -355,8 +355,8 @@ extern int                  ip6_dst_lookup(struct sock *sk,
  *     skb processing functions
  */
 
-extern int                     ip6_output(struct sk_buff *skb);
-extern int                     ip6_output2(struct sk_buff *skb);
+extern int                     ip6_output(struct sk_buff **pskb);
+extern int                     ip6_output2(struct sk_buff **pskb);
 extern int                     ip6_forward(struct sk_buff *skb);
 extern int                     ip6_input(struct sk_buff *skb);
 extern int                     ip6_mc_input(struct sk_buff *skb);
index e6d768a96be6ea102c9f071343fb83b3659cbac4..64704968507f6957a081a782b2efa2f1360a1cdc 100644 (file)
@@ -216,7 +216,7 @@ struct xfrm_type
        void                    (*destructor)(struct xfrm_state *);
        int                     (*input)(struct xfrm_state *, struct xfrm_decap_state *, struct sk_buff *skb);
        int                     (*post_input)(struct xfrm_state *, struct xfrm_decap_state *, struct sk_buff *skb);
-       int                     (*output)(struct sk_buff *skb);
+       int                     (*output)(struct sk_buff **pskb);
        /* Estimate maximal size of result of transformation of a dgram */
        u32                     (*get_max_size)(struct xfrm_state *, int size);
 };
index 9651cdfa87cfd4cc1d0f968f2243465a33bc8baf..4e91f1f00c62db86c6884875cf89e3e37f2128e9 100644 (file)
@@ -165,7 +165,7 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
                skb_pull(skb, VLAN_HLEN);
                skb->nh.raw += VLAN_HLEN;
        }
-       skb->dst->output(skb);
+       skb->dst->output(&skb);
        return 0;
 }
 
index dfd505c1c511197b8be25b7c27f3feb241807ace..fb20e1047f276fb0606fd844cbd8aefc9919de1f 100644 (file)
@@ -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);
index ba4856ccd57b60a5d97cf1600533a6706374a95e..1b0eaaa547fd673459669bbfcdf14baa9af43bd5 100644 (file)
@@ -100,15 +100,15 @@ out:
        spin_unlock(&dst_lock);
 }
 
-static int dst_discard(struct sk_buff *skb)
+static int dst_discard_in(struct sk_buff *skb)
 {
        kfree_skb(skb);
        return 0;
 }
 
-static int dst_blackhole(struct sk_buff *skb)
+static int dst_discard_out(struct sk_buff **pskb)
 {
-       kfree_skb(skb);
+       kfree_skb(*pskb);
        return 0;
 }
 
@@ -128,8 +128,8 @@ void * dst_alloc(struct dst_ops * ops)
        dst->ops = ops;
        dst->lastuse = jiffies;
        dst->path = dst;
-       dst->input = dst_discard;
-       dst->output = dst_blackhole;
+       dst->input = dst_discard_in;
+       dst->output = dst_discard_out;
 #if RT_CACHE_DEBUG >= 2 
        atomic_inc(&dst_total);
 #endif
@@ -143,8 +143,8 @@ static void ___dst_free(struct dst_entry * dst)
           protocol module is unloaded.
         */
        if (dst->dev == NULL || !(dst->dev->flags&IFF_UP)) {
-               dst->input = dst_discard;
-               dst->output = dst_blackhole;
+               dst->input = dst_discard_in;
+               dst->output = dst_discard_out;
        }
        dst->obsolete = 2;
 }
@@ -228,19 +228,19 @@ static int dst_dev_event(struct notifier_block *this, unsigned long event, void
                                   _race_ _condition_.
                                 */
                                if (event!=NETDEV_DOWN &&
-                                   dst->output == dst_blackhole) {
+                                   dst->output == dst_discard_out) {
                                        dst->dev = &loopback_dev;
                                        dev_put(dev);
                                        dev_hold(&loopback_dev);
-                                       dst->output = dst_discard;
+                                       dst->output = dst_discard_out;
                                        if (dst->neighbour && dst->neighbour->dev == dev) {
                                                dst->neighbour->dev = &loopback_dev;
                                                dev_put(dev);
                                                dev_hold(&loopback_dev);
                                        }
                                } else {
-                                       dst->input = dst_discard;
-                                       dst->output = dst_blackhole;
+                                       dst->input = dst_discard_in;
+                                       dst->output = dst_discard_out;
                                }
                        }
                }
index dd8b9a8eefa8ad090342e349ae3f1903e024703b..58632d189f52e3534f3e4a17909b442f450bd813 100644 (file)
@@ -504,14 +504,6 @@ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
        unsigned int verdict;
        int ret = 0;
 
-       if (skb->ip_summed == CHECKSUM_HW) {
-               if (outdev == NULL) {
-                       skb->ip_summed = CHECKSUM_NONE;
-               } else {
-                       skb_checksum_help(skb);
-               }
-       }
-
        /* We may already have this, but read-locks nest anyway */
        rcu_read_lock();
 
index d9628dbef46ce5452e573ff49abf55064313c36c..90d0583f56f4213de789ab3311fbcff3dd279a12 100644 (file)
@@ -684,8 +684,9 @@ out:
        return NET_RX_DROP;
 }
 
-static int dn_output(struct sk_buff *skb)
+static int dn_output(struct sk_buff **pskb)
 {
+       struct sk_buff *skb = *pskb;
        struct dst_entry *dst = skb->dst;
        struct dn_route *rt = (struct dn_route *)dst;
        struct net_device *dev = dst->dev;
@@ -796,6 +797,11 @@ static int dn_rt_bug(struct sk_buff *skb)
        return NET_RX_BAD;
 }
 
+static int dn_rt_bug_out(struct sk_buff **pskb)
+{
+       return dn_rt_bug(*pskb);
+}
+
 static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res)
 {
        struct dn_fib_info *fi = res->fi;
@@ -1387,7 +1393,7 @@ make_route:
        rt->u.dst.neighbour = neigh;
        rt->u.dst.dev = out_dev;
        rt->u.dst.lastuse = jiffies;
-       rt->u.dst.output = dn_rt_bug;
+       rt->u.dst.output = dn_rt_bug_out;
        switch(res.type) {
                case RTN_UNICAST:
                        rt->u.dst.input = dn_forward;
index d3395b048e86eeacd77293b2c11b91ef255b9a33..0d62398f71a6bfcd3a1291800334bf90e72af23e 100644 (file)
@@ -54,10 +54,10 @@ static int ip_clear_mutable_options(struct iphdr *iph, u32 *daddr)
        return 0;
 }
 
-static int ah_output(struct sk_buff *skb)
+static int ah_output(struct sk_buff **pskb)
 {
        int err;
-       struct dst_entry *dst = skb->dst;
+       struct dst_entry *dst = (*pskb)->dst;
        struct xfrm_state *x  = dst->xfrm;
        struct iphdr *iph, *top_iph;
        struct ip_auth_hdr *ah;
@@ -67,23 +67,24 @@ static int ah_output(struct sk_buff *skb)
                char            buf[60];
        } tmp_iph;
 
-       if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
-               err = -EINVAL;
-               goto error_nolock;
+       if ((*pskb)->ip_summed == CHECKSUM_HW) {
+               err = skb_checksum_help(pskb, 0);
+               if (err)
+                       goto error_nolock;
        }
 
        spin_lock_bh(&x->lock);
-       err = xfrm_check_output(x, skb, AF_INET);
+       err = xfrm_check_output(x, *pskb, AF_INET);
        if (err)
                goto error;
 
-       iph = skb->nh.iph;
+       iph = (*pskb)->nh.iph;
        if (x->props.mode) {
-               top_iph = (struct iphdr*)skb_push(skb, x->props.header_len);
+               top_iph = (struct iphdr*)skb_push(*pskb, x->props.header_len);
                top_iph->ihl = 5;
                top_iph->version = 4;
                top_iph->tos = 0;
-               top_iph->tot_len = htons(skb->len);
+               top_iph->tot_len = htons((*pskb)->len);
                top_iph->frag_off = 0;
                if (!(iph->frag_off&htons(IP_DF)))
                        __ip_select_ident(top_iph, dst, 0);
@@ -95,12 +96,12 @@ static int ah_output(struct sk_buff *skb)
                ah = (struct ip_auth_hdr*)(top_iph+1);
                ah->nexthdr = IPPROTO_IPIP;
        } else {
-               memcpy(&tmp_iph, skb->data, iph->ihl*4);
-               top_iph = (struct iphdr*)skb_push(skb, x->props.header_len);
+               memcpy(&tmp_iph, (*pskb)->data, iph->ihl*4);
+               top_iph = (struct iphdr*)skb_push(*pskb, x->props.header_len);
                memcpy(top_iph, &tmp_iph, iph->ihl*4);
                iph = &tmp_iph.iph;
                top_iph->tos = 0;
-               top_iph->tot_len = htons(skb->len);
+               top_iph->tot_len = htons((*pskb)->len);
                top_iph->frag_off = 0;
                top_iph->ttl = 0;
                top_iph->protocol = IPPROTO_AH;
@@ -120,14 +121,14 @@ static int ah_output(struct sk_buff *skb)
        ah->reserved = 0;
        ah->spi = x->id.spi;
        ah->seq_no = htonl(++x->replay.oseq);
-       ahp->icv(ahp, skb, ah->auth_data);
+       ahp->icv(ahp, *pskb, ah->auth_data);
        top_iph->tos = iph->tos;
        top_iph->ttl = iph->ttl;
        if (x->props.mode) {
                if (x->props.flags & XFRM_STATE_NOECN)
                        IP_ECN_clear(top_iph);
                top_iph->frag_off = iph->frag_off&~htons(IP_MF|IP_OFFSET);
-               memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+               memset(&(IPCB(*pskb)->opt), 0, sizeof(struct ip_options));
        } else {
                top_iph->frag_off = iph->frag_off;
                top_iph->daddr = iph->daddr;
@@ -136,12 +137,12 @@ static int ah_output(struct sk_buff *skb)
        }
        ip_send_check(top_iph);
 
-       skb->nh.raw = skb->data;
+       (*pskb)->nh.raw = (*pskb)->data;
 
-       x->curlft.bytes += skb->len;
+       x->curlft.bytes += (*pskb)->len;
        x->curlft.packets++;
        spin_unlock_bh(&x->lock);
-       if ((skb->dst = dst_pop(dst)) == NULL) {
+       if (((*pskb)->dst = dst_pop(dst)) == NULL) {
                err = -EHOSTUNREACH;
                goto error_nolock;
        }
@@ -150,7 +151,7 @@ static int ah_output(struct sk_buff *skb)
 error:
        spin_unlock_bh(&x->lock);
 error_nolock:
-       kfree_skb(skb);
+       kfree_skb(*pskb);
        return err;
 }
 
index d4696c1f01e38fc91fde17860a6886bfefec836a..4755e0dcc90d1d4d0dc636b9bbfd0123ababd7a4 100644 (file)
@@ -20,10 +20,10 @@ struct esp_decap_data {
        __u8            proto;
 };
 
-int esp_output(struct sk_buff *skb)
+int esp_output(struct sk_buff **pskb)
 {
        int err;
-       struct dst_entry *dst = skb->dst;
+       struct dst_entry *dst = (*pskb)->dst;
        struct xfrm_state *x  = dst->xfrm;
        struct iphdr *iph, *top_iph;
        struct ip_esp_hdr *esph;
@@ -42,28 +42,28 @@ int esp_output(struct sk_buff *skb)
                char            buf[60];
        } tmp_iph;
 
-       /* First, if the skb is not checksummed, complete checksum. */
-       if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
-               err = -EINVAL;
-               goto error_nolock;
+       if ((*pskb)->ip_summed == CHECKSUM_HW) {
+               err = skb_checksum_help(pskb, 0);
+               if (err)
+                       goto error_nolock;
        }
 
        spin_lock_bh(&x->lock);
-       err = xfrm_check_output(x, skb, AF_INET);
+       err = xfrm_check_output(x, *pskb, AF_INET);
        if (err)
                goto error;
        err = -ENOMEM;
 
        /* Strip IP header in transport mode. Save it. */
        if (!x->props.mode) {
-               iph = skb->nh.iph;
+               iph = (*pskb)->nh.iph;
                memcpy(&tmp_iph, iph, iph->ihl*4);
-               __skb_pull(skb, iph->ihl*4);
+               __skb_pull(*pskb, iph->ihl*4);
        }
        /* Now skb is pure payload to encrypt */
 
        /* Round to block size */
-       clen = skb->len;
+       clen = (*pskb)->len;
 
        esp = x->data;
        alen = esp->auth.icv_trunc_len;
@@ -73,23 +73,23 @@ int esp_output(struct sk_buff *skb)
        if (esp->conf.padlen)
                clen = (clen + esp->conf.padlen-1)&~(esp->conf.padlen-1);
 
-       if ((nfrags = skb_cow_data(skb, clen-skb->len+alen, &trailer)) < 0)
+       if ((nfrags = skb_cow_data(*pskb, clen-(*pskb)->len+alen, &trailer)) < 0)
                goto error;
 
        /* Fill padding... */
        do {
                int i;
-               for (i=0; i<clen-skb->len - 2; i++)
+               for (i=0; i<clen-(*pskb)->len - 2; i++)
                        *(u8*)(trailer->tail + i) = i+1;
        } while (0);
-       *(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;
-       pskb_put(skb, trailer, clen - skb->len);
+       *(u8*)(trailer->tail + clen-(*pskb)->len - 2) = (clen - (*pskb)->len)-2;
+       pskb_put(*pskb, trailer, clen - (*pskb)->len);
 
        encap = x->encap;
 
-       iph = skb->nh.iph;
+       iph = (*pskb)->nh.iph;
        if (x->props.mode) {
-               top_iph = (struct iphdr*)skb_push(skb, x->props.header_len);
+               top_iph = (struct iphdr*)skb_push(*pskb, x->props.header_len);
                esph = (struct ip_esp_hdr*)(top_iph+1);
                if (encap && encap->encap_type) {
                        switch (encap->encap_type) {
@@ -121,7 +121,7 @@ int esp_output(struct sk_buff *skb)
                top_iph->tos = iph->tos;        /* DS disclosed */
                if (x->props.flags & XFRM_STATE_NOECN)
                        IP_ECN_clear(top_iph);
-               top_iph->tot_len = htons(skb->len + alen);
+               top_iph->tot_len = htons((*pskb)->len + alen);
                top_iph->frag_off = iph->frag_off&htons(IP_DF);
                if (!(top_iph->frag_off))
                        ip_select_ident(top_iph, dst, 0);
@@ -129,10 +129,10 @@ int esp_output(struct sk_buff *skb)
                top_iph->check = 0;
                top_iph->saddr = x->props.saddr.a4;
                top_iph->daddr = x->id.daddr.a4;
-               memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+               memset(&(IPCB(*pskb)->opt), 0, sizeof(struct ip_options));
        } else {
-               esph = (struct ip_esp_hdr*)skb_push(skb, x->props.header_len);
-               top_iph = (struct iphdr*)skb_push(skb, iph->ihl*4);
+               esph = (struct ip_esp_hdr*)skb_push(*pskb, x->props.header_len);
+               top_iph = (struct iphdr*)skb_push(*pskb, iph->ihl*4);
                memcpy(top_iph, &tmp_iph, iph->ihl*4);
                if (encap && encap->encap_type) {
                        switch (encap->encap_type) {
@@ -159,7 +159,7 @@ int esp_output(struct sk_buff *skb)
                } else
                        top_iph->protocol = IPPROTO_ESP;
                iph = &tmp_iph.iph;
-               top_iph->tot_len = htons(skb->len + alen);
+               top_iph->tot_len = htons((*pskb)->len + alen);
                top_iph->check = 0;
                top_iph->frag_off = iph->frag_off;
                *(u8*)(trailer->tail - 1) = iph->protocol;
@@ -169,7 +169,7 @@ int esp_output(struct sk_buff *skb)
        if (encap && uh) {
                uh->source = encap->encap_sport;
                uh->dest = encap->encap_dport;
-               uh->len = htons(skb->len + alen - sizeof(struct iphdr));
+               uh->len = htons((*pskb)->len + alen - sizeof(struct iphdr));
                uh->check = 0;
        }
 
@@ -188,7 +188,7 @@ int esp_output(struct sk_buff *skb)
                        if (!sg)
                                goto error;
                }
-               skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, clen);
+               skb_to_sgvec(*pskb, sg, esph->enc_data+esp->conf.ivlen-(*pskb)->data, clen);
                crypto_cipher_encrypt(tfm, sg, sg, clen);
                if (unlikely(sg != sgbuf))
                        kfree(sg);
@@ -200,19 +200,19 @@ int esp_output(struct sk_buff *skb)
        }
 
        if (esp->auth.icv_full_len) {
-               esp->auth.icv(esp, skb, (u8*)esph-skb->data,
+               esp->auth.icv(esp, *pskb, (u8*)esph-(*pskb)->data,
                              sizeof(struct ip_esp_hdr) + esp->conf.ivlen+clen, trailer->tail);
-               pskb_put(skb, trailer, alen);
+               pskb_put(*pskb, trailer, alen);
        }
 
        ip_send_check(top_iph);
 
-       skb->nh.raw = skb->data;
+       (*pskb)->nh.raw = (*pskb)->data;
 
-       x->curlft.bytes += skb->len;
+       x->curlft.bytes += (*pskb)->len;
        x->curlft.packets++;
        spin_unlock_bh(&x->lock);
-       if ((skb->dst = dst_pop(dst)) == NULL) {
+       if (((*pskb)->dst = dst_pop(dst)) == NULL) {
                err = -EHOSTUNREACH;
                goto error_nolock;
        }
@@ -221,7 +221,7 @@ int esp_output(struct sk_buff *skb)
 error:
        spin_unlock_bh(&x->lock);
 error_nolock:
-       kfree_skb(skb);
+       kfree_skb(*pskb);
        return err;
 }
 
index e9b7d78c8e1cdb6798d0927bb69e8bf0e082e690..60fc5091c0ef690eba8540ab7548bc04922337e4 100644 (file)
@@ -223,8 +223,9 @@ int ip_finish_output(struct sk_buff *skb)
                       ip_finish_output2);
 }
 
-int ip_mc_output(struct sk_buff *skb)
+int ip_mc_output(struct sk_buff **pskb)
 {
+       struct sk_buff *skb = *pskb;
        struct sock *sk = skb->sk;
        struct rtable *rt = (struct rtable*)skb->dst;
        struct net_device *dev = rt->u.dst.dev;
@@ -283,8 +284,10 @@ int ip_mc_output(struct sk_buff *skb)
                return ip_finish_output(skb);
 }
 
-int ip_output(struct sk_buff *skb)
+int ip_output(struct sk_buff **pskb)
 {
+       struct sk_buff *skb = *pskb;
+
        IP_INC_STATS(OutRequests);
 
        if ((skb->len > dst_pmtu(skb->dst) || skb_shinfo(skb)->frag_list) &&
index 57d84fa777b4a342372d684613efd4ab8f885f08..e20c7e43b2155ba96aeb8a0060b93b24ff04ec61 100644 (file)
@@ -143,10 +143,10 @@ static void ipcomp_tunnel_encap(struct xfrm_state *x, struct sk_buff *skb)
        skb->nh.raw = skb->data;
 }
 
-static int ipcomp_output(struct sk_buff *skb)
+static int ipcomp_output(struct sk_buff **pskb)
 {
        int err;
-       struct dst_entry *dst = skb->dst;
+       struct dst_entry *dst = (*pskb)->dst;
        struct xfrm_state *x = dst->xfrm;
        struct iphdr *iph, *top_iph;
        struct ip_comp_hdr *ipch;
@@ -157,25 +157,26 @@ static int ipcomp_output(struct sk_buff *skb)
        } tmp_iph;
        int hdr_len = 0;
 
-       if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
-               err = -EINVAL;
-               goto error_nolock;
+       if ((*pskb)->ip_summed == CHECKSUM_HW) {
+               err = skb_checksum_help(pskb, 0);
+               if (err)
+                       goto error_nolock;
        }
 
        spin_lock_bh(&x->lock);
-       err = xfrm_check_output(x, skb, AF_INET);
+       err = xfrm_check_output(x, *pskb, AF_INET);
        if (err)
                goto error;
 
        /* Don't bother compressing */
        if (!x->props.mode) {
-               iph = skb->nh.iph;
+               iph = (*pskb)->nh.iph;
                hdr_len = iph->ihl * 4;
        }
-       if ((skb->len - hdr_len) < ipcd->threshold) {
+       if (((*pskb)->len - hdr_len) < ipcd->threshold) {
                if (x->props.mode) {
-                       ipcomp_tunnel_encap(x, skb);
-                       iph = skb->nh.iph;
+                       ipcomp_tunnel_encap(x, *pskb);
+                       iph = (*pskb)->nh.iph;
                        iph->protocol = IPPROTO_IPIP;
                        ip_send_check(iph);
                }
@@ -183,19 +184,19 @@ static int ipcomp_output(struct sk_buff *skb)
        }
 
        if (x->props.mode) 
-               ipcomp_tunnel_encap(x, skb);
+               ipcomp_tunnel_encap(x, *pskb);
 
-       if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
-           skb_linearize(skb, GFP_ATOMIC) != 0) {
+       if ((skb_is_nonlinear(*pskb) || skb_cloned(*pskb)) &&
+           skb_linearize(*pskb, GFP_ATOMIC) != 0) {
                err = -ENOMEM;
                goto error;
        }
        
-       err = ipcomp_compress(x, skb);
+       err = ipcomp_compress(x, *pskb);
        if (err) {
                if (err == -EMSGSIZE) {
                        if (x->props.mode) {
-                               iph = skb->nh.iph;
+                               iph = (*pskb)->nh.iph;
                                iph->protocol = IPPROTO_IPIP;
                                ip_send_check(iph);
                        }
@@ -205,14 +206,14 @@ static int ipcomp_output(struct sk_buff *skb)
        }
 
        /* Install ipcomp header, convert into ipcomp datagram. */
-       iph = skb->nh.iph;
+       iph = (*pskb)->nh.iph;
        memcpy(&tmp_iph, iph, iph->ihl * 4);
-       top_iph = (struct iphdr *)skb_push(skb, sizeof(struct ip_comp_hdr));
+       top_iph = (struct iphdr *)skb_push(*pskb, sizeof(struct ip_comp_hdr));
        memcpy(top_iph, &tmp_iph, iph->ihl * 4);
        iph = top_iph;
        if (x->props.mode && (x->props.flags & XFRM_STATE_NOECN))
                IP_ECN_clear(iph);
-       iph->tot_len = htons(skb->len);
+       iph->tot_len = htons((*pskb)->len);
        iph->protocol = IPPROTO_COMP;
        iph->check = 0;
        ipch = (struct ip_comp_hdr *)((char *)iph + iph->ihl * 4);
@@ -220,14 +221,14 @@ static int ipcomp_output(struct sk_buff *skb)
        ipch->flags = 0;
        ipch->cpi = htons((u16 )ntohl(x->id.spi));
        ip_send_check(iph);
-       skb->nh.raw = skb->data;
+       (*pskb)->nh.raw = (*pskb)->data;
 
 out_ok:
-       x->curlft.bytes += skb->len;
+       x->curlft.bytes += (*pskb)->len;
        x->curlft.packets++;
        spin_unlock_bh(&x->lock);
        
-       if ((skb->dst = dst_pop(dst)) == NULL) {
+       if (((*pskb)->dst = dst_pop(dst)) == NULL) {
                err = -EHOSTUNREACH;
                goto error_nolock;
        }
@@ -238,7 +239,7 @@ out_exit:
 error:
        spin_unlock_bh(&x->lock);
 error_nolock:
-       kfree_skb(skb);
+       kfree_skb(*pskb);
        goto out_exit;
 }
 
index 0b26447c72d5a4a4f5add8bd9ad3163d0767cad1..f7146f31b6c3de2bf42765aab0d37fb74b935f17 100644 (file)
@@ -69,7 +69,8 @@ fw_in(unsigned int hooknum,
        /* Assume worse case: any hook could change packet */
        (*pskb)->nfcache |= NFC_UNKNOWN | NFC_ALTERED;
        if ((*pskb)->ip_summed == CHECKSUM_HW)
-               (*pskb)->ip_summed = CHECKSUM_NONE;
+               if (skb_checksum_help(pskb, (out == NULL)))
+                       return NF_DROP;
 
        switch (hooknum) {
        case NF_IP_PRE_ROUTING:
index c4fe69b6552500438acadfe4c0c2703dbb3904a6..a84453f899fe65be334345bcdef55e463a56f526 100644 (file)
@@ -85,7 +85,8 @@ ip_nat_fn(unsigned int hooknum,
 
        /* If we had a hardware checksum before, it's now invalid */
        if ((*pskb)->ip_summed == CHECKSUM_HW)
-               (*pskb)->ip_summed = CHECKSUM_NONE;
+               if (skb_checksum_help(pskb, (out == NULL)))
+                       return NF_DROP;
 
        ct = ip_conntrack_get(*pskb, &ctinfo);
        /* Can't track?  It's not due to stress, or conntrack would
index 341e845f4d4b69ec4771798d7f8ead7ab4134490..8ca402564f5e0b662b76ce708a1f31f25d572c29 100644 (file)
@@ -50,7 +50,7 @@ set_ect_ip(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
 
 /* Return 0 if there was an error. */
 static inline int
-set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
+set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo, int inward)
 {
        struct tcphdr tcph;
        u_int16_t diffs[2];
@@ -74,11 +74,15 @@ set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
                if (!skb_ip_make_writable(pskb,
                                          (*pskb)->nh.iph->ihl*4+sizeof(tcph)))
                        return 0;
-               tcph.check = csum_fold(csum_partial((char *)diffs,
-                                                   sizeof(diffs),
-                                                   tcph.check^0xFFFF));
+               if ((*pskb)->ip_summed != CHECKSUM_HW)
+                       tcph.check = csum_fold(csum_partial((char *)diffs,
+                                              sizeof(diffs),
+                                              tcph.check^0xFFFF));
                memcpy((*pskb)->data + (*pskb)->nh.iph->ihl*4,
                       &tcph, sizeof(tcph));
+               if ((*pskb)->ip_summed == CHECKSUM_HW)
+                       if (skb_checksum_help(pskb, inward))
+                               return 0;
                (*pskb)->nfcache |= NFC_ALTERED;
        }
        return 1;
@@ -100,7 +104,7 @@ target(struct sk_buff **pskb,
 
        if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR)
            && (*pskb)->nh.iph->protocol == IPPROTO_TCP)
-               if (!set_ect_tcp(pskb, einfo))
+               if (!set_ect_tcp(pskb, einfo, (out == NULL)))
                        return NF_DROP;
 
        return IPT_CONTINUE;
index 369f84b4c0584a28eb3f67bed3882545cf02a848..ab7a78f32b876f99f268bb41da8a929f9ac6c480 100644 (file)
@@ -187,7 +187,9 @@ ipt_tcpmss_target(struct sk_buff **pskb,
 
  retmodified:
        /* If we had a hardware checksum before, it's now invalid */
-       (*pskb)->ip_summed = CHECKSUM_NONE;
+       if ((*pskb)->ip_summed == CHECKSUM_HW)
+               if (skb_checksum_help(pskb, 0))
+                       return NF_DROP;
        (*pskb)->nfcache |= NFC_UNKNOWN | NFC_ALTERED;
        return IPT_CONTINUE;
 }
index 6e7cbd378e8e24bc7c968bfcf507f74b8fcb3127..03300f6a9f355820a8f740dc7e4d518f86685176 100644 (file)
@@ -1347,8 +1347,10 @@ static void ipv4_link_failure(struct sk_buff *skb)
                dst_set_expires(&rt->u.dst, 0);
 }
 
-static int ip_rt_bug(struct sk_buff *skb)
+static int ip_rt_bug(struct sk_buff **pskb)
 {
+       struct sk_buff *skb = *pskb;
+
        printk(KERN_DEBUG "ip_rt_bug: %u.%u.%u.%u -> %u.%u.%u.%u, %s\n",
                NIPQUAD(skb->nh.iph->saddr), NIPQUAD(skb->nh.iph->daddr),
                skb->dev ? skb->dev->name : "?");
index accc0c8d6543b055a3238d2bc08fab35eb400135..3c9d4aaa21be383d66481c5fd7ecf70793fbce0a 100644 (file)
@@ -33,8 +33,9 @@ out:
        return ret;
 }
 
-static int ipip_output(struct sk_buff *skb)
+static int ipip_output(struct sk_buff **pskb)
 {
+       struct sk_buff *skb = *pskb;
        struct dst_entry *dst = skb->dst;
        struct xfrm_state *x = dst->xfrm;
        struct iphdr *iph, *top_iph;
index 81c5d9766b7c8f6d58452a03a8a096b4d5e949c2..6fde2c1c37f384dc4978c5dfefc7b13884110896 100644 (file)
@@ -144,11 +144,11 @@ static int ipv6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int d
        return nexthdr;
 }
 
-int ah6_output(struct sk_buff *skb)
+int ah6_output(struct sk_buff **pskb)
 {
        int err;
        int hdr_len = sizeof(struct ipv6hdr);
-       struct dst_entry *dst = skb->dst;
+       struct dst_entry *dst = (*pskb)->dst;
        struct xfrm_state *x  = dst->xfrm;
        struct ipv6hdr *iph = NULL;
        struct ip_auth_hdr *ah;
@@ -156,54 +156,55 @@ int ah6_output(struct sk_buff *skb)
        u16 nh_offset = 0;
        u8 nexthdr;
 
-       if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
-               err = -EINVAL;
-               goto error_nolock;
+       if ((*pskb)->ip_summed == CHECKSUM_HW) {
+               err = skb_checksum_help(pskb, 0);
+               if (err)
+                       goto error_nolock;
        }
 
        spin_lock_bh(&x->lock);
-       err = xfrm_check_output(x, skb, AF_INET6);
+       err = xfrm_check_output(x, *pskb, AF_INET6);
        if (err)
                goto error;
 
        if (x->props.mode) {
-               iph = skb->nh.ipv6h;
-               skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, x->props.header_len);
-               skb->nh.ipv6h->version = 6;
-               skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
-               skb->nh.ipv6h->nexthdr = IPPROTO_AH;
-               ipv6_addr_copy(&skb->nh.ipv6h->saddr,
+               iph = (*pskb)->nh.ipv6h;
+               (*pskb)->nh.ipv6h = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len);
+               (*pskb)->nh.ipv6h->version = 6;
+               (*pskb)->nh.ipv6h->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
+               (*pskb)->nh.ipv6h->nexthdr = IPPROTO_AH;
+               ipv6_addr_copy(&(*pskb)->nh.ipv6h->saddr,
                               (struct in6_addr *) &x->props.saddr);
-               ipv6_addr_copy(&skb->nh.ipv6h->daddr,
+               ipv6_addr_copy(&(*pskb)->nh.ipv6h->daddr,
                               (struct in6_addr *) &x->id.daddr);
-               ah = (struct ip_auth_hdr*)(skb->nh.ipv6h+1);
+               ah = (struct ip_auth_hdr*)((*pskb)->nh.ipv6h+1);
                ah->nexthdr = IPPROTO_IPV6;
        } else {
-               hdr_len = skb->h.raw - skb->nh.raw;
+               hdr_len = (*pskb)->h.raw - (*pskb)->nh.raw;
                iph = kmalloc(hdr_len, GFP_ATOMIC);
                if (!iph) {
                        err = -ENOMEM;
                        goto error;
                }
-               memcpy(iph, skb->data, hdr_len);
-               skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, x->props.header_len);
-               memcpy(skb->nh.ipv6h, iph, hdr_len);
-               nexthdr = ipv6_clear_mutable_options(skb, &nh_offset, XFRM_POLICY_OUT);
+               memcpy(iph, (*pskb)->data, hdr_len);
+               (*pskb)->nh.ipv6h = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len);
+               memcpy((*pskb)->nh.ipv6h, iph, hdr_len);
+               nexthdr = ipv6_clear_mutable_options(*pskb, &nh_offset, XFRM_POLICY_OUT);
                if (nexthdr == 0)
                        goto error_free_iph;
 
-               skb->nh.raw[nh_offset] = IPPROTO_AH;
-               skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
-               ah = (struct ip_auth_hdr*)(skb->nh.raw+hdr_len);
-               skb->h.raw = (unsigned char*) ah;
+               (*pskb)->nh.raw[nh_offset] = IPPROTO_AH;
+               (*pskb)->nh.ipv6h->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
+               ah = (struct ip_auth_hdr*)((*pskb)->nh.raw+hdr_len);
+               (*pskb)->h.raw = (unsigned char*) ah;
                ah->nexthdr = nexthdr;
        }
 
-       skb->nh.ipv6h->priority    = 0;
-       skb->nh.ipv6h->flow_lbl[0] = 0;
-       skb->nh.ipv6h->flow_lbl[1] = 0;
-       skb->nh.ipv6h->flow_lbl[2] = 0;
-       skb->nh.ipv6h->hop_limit    = 0;
+       (*pskb)->nh.ipv6h->priority    = 0;
+       (*pskb)->nh.ipv6h->flow_lbl[0] = 0;
+       (*pskb)->nh.ipv6h->flow_lbl[1] = 0;
+       (*pskb)->nh.ipv6h->flow_lbl[2] = 0;
+       (*pskb)->nh.ipv6h->hop_limit    = 0;
 
        ahp = x->data;
        ah->hdrlen  = (XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + 
@@ -212,29 +213,29 @@ int ah6_output(struct sk_buff *skb)
        ah->reserved = 0;
        ah->spi = x->id.spi;
        ah->seq_no = htonl(++x->replay.oseq);
-       ahp->icv(ahp, skb, ah->auth_data);
+       ahp->icv(ahp, *pskb, ah->auth_data);
 
        if (x->props.mode) {
-               skb->nh.ipv6h->hop_limit   = iph->hop_limit;
-               skb->nh.ipv6h->priority    = iph->priority;     
-               skb->nh.ipv6h->flow_lbl[0] = iph->flow_lbl[0];
-               skb->nh.ipv6h->flow_lbl[1] = iph->flow_lbl[1];
-               skb->nh.ipv6h->flow_lbl[2] = iph->flow_lbl[2];
+               (*pskb)->nh.ipv6h->hop_limit   = iph->hop_limit;
+               (*pskb)->nh.ipv6h->priority    = iph->priority;         
+               (*pskb)->nh.ipv6h->flow_lbl[0] = iph->flow_lbl[0];
+               (*pskb)->nh.ipv6h->flow_lbl[1] = iph->flow_lbl[1];
+               (*pskb)->nh.ipv6h->flow_lbl[2] = iph->flow_lbl[2];
                if (x->props.flags & XFRM_STATE_NOECN)
-                       IP6_ECN_clear(skb->nh.ipv6h);
+                       IP6_ECN_clear((*pskb)->nh.ipv6h);
        } else {
-               memcpy(skb->nh.ipv6h, iph, hdr_len);
-               skb->nh.raw[nh_offset] = IPPROTO_AH;
-               skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
+               memcpy((*pskb)->nh.ipv6h, iph, hdr_len);
+               (*pskb)->nh.raw[nh_offset] = IPPROTO_AH;
+               (*pskb)->nh.ipv6h->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
                kfree (iph);
        }
 
-       skb->nh.raw = skb->data;
+       (*pskb)->nh.raw = (*pskb)->data;
 
-       x->curlft.bytes += skb->len;
+       x->curlft.bytes += (*pskb)->len;
        x->curlft.packets++;
        spin_unlock_bh(&x->lock);
-       if ((skb->dst = dst_pop(dst)) == NULL) {
+       if (((*pskb)->dst = dst_pop(dst)) == NULL) {
                err = -EHOSTUNREACH;
                goto error_nolock;
        }
@@ -244,7 +245,7 @@ error_free_iph:
 error:
        spin_unlock_bh(&x->lock);
 error_nolock:
-       kfree_skb(skb);
+       kfree_skb(*pskb);
        return err;
 }
 
index dc8bfac22d46d77ae0bc48743f302807cd43313d..164880299b404cb71de5e968b9aedf9ae9aa11c5 100644 (file)
 
 #define MAX_SG_ONSTACK 4
 
-int esp6_output(struct sk_buff *skb)
+int esp6_output(struct sk_buff **pskb)
 {
        int err;
        int hdr_len = 0;
-       struct dst_entry *dst = skb->dst;
+       struct dst_entry *dst = (*pskb)->dst;
        struct xfrm_state *x  = dst->xfrm;
        struct ipv6hdr *iph = NULL, *top_iph;
        struct ipv6_esp_hdr *esph;
@@ -58,14 +58,14 @@ int esp6_output(struct sk_buff *skb)
        u8 *prevhdr;
        u8 nexthdr = 0;
 
-       /* First, if the skb is not checksummed, complete checksum. */
-       if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
-               err = -EINVAL;
-               goto error_nolock;
+       if ((*pskb)->ip_summed == CHECKSUM_HW) {
+               err = skb_checksum_help(pskb, 0);
+               if (err)
+                       goto error_nolock;
        }
 
        spin_lock_bh(&x->lock);
-       err = xfrm_check_output(x, skb, AF_INET6);
+       err = xfrm_check_output(x, *pskb, AF_INET6);
        if (err)
                goto error;
        err = -ENOMEM;
@@ -73,7 +73,7 @@ int esp6_output(struct sk_buff *skb)
        /* Strip IP header in transport mode. Save it. */
 
        if (!x->props.mode) {
-               hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
+               hdr_len = ip6_find_1stfragopt(*pskb, &prevhdr);
                nexthdr = *prevhdr;
                *prevhdr = IPPROTO_ESP;
                iph = kmalloc(hdr_len, GFP_ATOMIC);
@@ -81,14 +81,14 @@ int esp6_output(struct sk_buff *skb)
                        err = -ENOMEM;
                        goto error;
                }
-               memcpy(iph, skb->nh.raw, hdr_len);
-               __skb_pull(skb, hdr_len);
+               memcpy(iph, (*pskb)->nh.raw, hdr_len);
+               __skb_pull(*pskb, hdr_len);
        }
 
        /* Now skb is pure payload to encrypt */
 
        /* Round to block size */
-       clen = skb->len;
+       clen = (*pskb)->len;
 
        esp = x->data;
        alen = esp->auth.icv_trunc_len;
@@ -98,7 +98,7 @@ int esp6_output(struct sk_buff *skb)
        if (esp->conf.padlen)
                clen = (clen + esp->conf.padlen-1)&~(esp->conf.padlen-1);
 
-       if ((nfrags = skb_cow_data(skb, clen-skb->len+alen, &trailer)) < 0) {
+       if ((nfrags = skb_cow_data(*pskb, clen-(*pskb)->len+alen, &trailer)) < 0) {
                if (!x->props.mode && iph) kfree(iph);
                goto error;
        }
@@ -106,15 +106,15 @@ int esp6_output(struct sk_buff *skb)
        /* Fill padding... */
        do {
                int i;
-               for (i=0; i<clen-skb->len - 2; i++)
+               for (i=0; i<clen-(*pskb)->len - 2; i++)
                        *(u8*)(trailer->tail + i) = i+1;
        } while (0);
-       *(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;
-       pskb_put(skb, trailer, clen - skb->len);
+       *(u8*)(trailer->tail + clen-(*pskb)->len - 2) = (clen - (*pskb)->len)-2;
+       pskb_put(*pskb, trailer, clen - (*pskb)->len);
 
        if (x->props.mode) {
-               iph = skb->nh.ipv6h;
-               top_iph = (struct ipv6hdr*)skb_push(skb, x->props.header_len);
+               iph = (*pskb)->nh.ipv6h;
+               top_iph = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len);
                esph = (struct ipv6_esp_hdr*)(top_iph+1);
                *(u8*)(trailer->tail - 1) = IPPROTO_IPV6;
                top_iph->version = 6;
@@ -125,19 +125,19 @@ int esp6_output(struct sk_buff *skb)
                if (x->props.flags & XFRM_STATE_NOECN)
                        IP6_ECN_clear(top_iph);
                top_iph->nexthdr = IPPROTO_ESP;
-               top_iph->payload_len = htons(skb->len + alen - sizeof(struct ipv6hdr));
+               top_iph->payload_len = htons((*pskb)->len + alen - sizeof(struct ipv6hdr));
                top_iph->hop_limit = iph->hop_limit;
                ipv6_addr_copy(&top_iph->saddr,
                               (struct in6_addr *)&x->props.saddr);
                ipv6_addr_copy(&top_iph->daddr,
                               (struct in6_addr *)&x->id.daddr);
        } else { 
-               esph = (struct ipv6_esp_hdr*)skb_push(skb, x->props.header_len);
-               skb->h.raw = (unsigned char*)esph;
-               top_iph = (struct ipv6hdr*)skb_push(skb, hdr_len);
+               esph = (struct ipv6_esp_hdr*)skb_push(*pskb, x->props.header_len);
+               (*pskb)->h.raw = (unsigned char*)esph;
+               top_iph = (struct ipv6hdr*)skb_push(*pskb, hdr_len);
                memcpy(top_iph, iph, hdr_len);
                kfree(iph);
-               top_iph->payload_len = htons(skb->len + alen - sizeof(struct ipv6hdr));
+               top_iph->payload_len = htons((*pskb)->len + alen - sizeof(struct ipv6hdr));
                *(u8*)(trailer->tail - 1) = nexthdr;
        }
 
@@ -156,7 +156,7 @@ int esp6_output(struct sk_buff *skb)
                        if (!sg)
                                goto error;
                }
-               skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, clen);
+               skb_to_sgvec(*pskb, sg, esph->enc_data+esp->conf.ivlen-(*pskb)->data, clen);
                crypto_cipher_encrypt(tfm, sg, sg, clen);
                if (unlikely(sg != sgbuf))
                        kfree(sg);
@@ -168,17 +168,17 @@ int esp6_output(struct sk_buff *skb)
        }
 
        if (esp->auth.icv_full_len) {
-               esp->auth.icv(esp, skb, (u8*)esph-skb->data,
+               esp->auth.icv(esp, *pskb, (u8*)esph-(*pskb)->data,
                        sizeof(struct ipv6_esp_hdr) + esp->conf.ivlen+clen, trailer->tail);
-               pskb_put(skb, trailer, alen);
+               pskb_put(*pskb, trailer, alen);
        }
 
-       skb->nh.raw = skb->data;
+       (*pskb)->nh.raw = (*pskb)->data;
 
-       x->curlft.bytes += skb->len;
+       x->curlft.bytes += (*pskb)->len;
        x->curlft.packets++;
        spin_unlock_bh(&x->lock);
-       if ((skb->dst = dst_pop(dst)) == NULL) {
+       if (((*pskb)->dst = dst_pop(dst)) == NULL) {
                err = -EHOSTUNREACH;
                goto error_nolock;
        }
@@ -187,7 +187,7 @@ int esp6_output(struct sk_buff *skb)
 error:
        spin_unlock_bh(&x->lock);
 error_nolock:
-       kfree_skb(skb);
+       kfree_skb(*pskb);
        return err;
 }
 
index 87f0ef0eb817d0d3ac0381143d2e96f4a301a2fd..b9e7b26c5c29e6290e9f8c27ffc13869df09ca40 100644 (file)
@@ -55,7 +55,7 @@
 #include <net/icmp.h>
 #include <net/xfrm.h>
 
-static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*));
+static int ip6_fragment(struct sk_buff **pskb, int (*output)(struct sk_buff**));
 
 static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr)
 {
@@ -107,8 +107,9 @@ static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
 }
 
 
-int ip6_output2(struct sk_buff *skb)
+int ip6_output2(struct sk_buff **pskb)
 {
+       struct sk_buff *skb = *pskb;
        struct dst_entry *dst = skb->dst;
        struct net_device *dev = dst->dev;
 
@@ -144,12 +145,14 @@ int ip6_output2(struct sk_buff *skb)
        return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish);
 }
 
-int ip6_output(struct sk_buff *skb)
+int ip6_output(struct sk_buff **pskb)
 {
+       struct sk_buff *skb = *pskb;
+
        if ((skb->len > dst_pmtu(skb->dst) || skb_shinfo(skb)->frag_list))
-               return ip6_fragment(skb, ip6_output2);
+               return ip6_fragment(pskb, ip6_output2);
        else
-               return ip6_output2(skb);
+               return ip6_output2(pskb);
 }
 
 #ifdef CONFIG_NETFILTER
@@ -513,11 +516,11 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
        return offset;
 }
 
-static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
+static int ip6_fragment(struct sk_buff **pskb, int (*output)(struct sk_buff**))
 {
        struct net_device *dev;
+       struct sk_buff *frag, *skb = *pskb;
        struct rt6_info *rt = (struct rt6_info*)skb->dst;
-       struct sk_buff *frag;
        struct ipv6hdr *tmp_hdr;
        struct frag_hdr *fh;
        unsigned int mtu, hlen, left, len;
@@ -604,11 +607,13 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
                                frag->nh.ipv6h->payload_len = htons(frag->len - sizeof(struct ipv6hdr));
                                ip6_copy_metadata(frag, skb);
                        }
-                       err = output(skb);
-
-                       if (err || !frag)
+                       err = output(pskb);
+                       if (err || !frag) {
+                               if (unlikely(skb != *pskb))
+                                       skb = *pskb;
                                break;
-
+                       }
+                       
                        skb = frag;
                        frag = skb->next;
                        skb->next = NULL;
@@ -721,7 +726,7 @@ slow_path:
 
                IP6_INC_STATS(FragCreates);
 
-               err = output(frag);
+               err = output(&frag);
                if (err)
                        goto fail;
        }
index 97db913bf1b33764581f61fca436b0fefc205424..b357db4cc84bc2d2d0d4680844879edcbbfdc609 100644 (file)
@@ -118,10 +118,10 @@ error_out:
        return err;
 }
 
-static int ipcomp6_output(struct sk_buff *skb)
+static int ipcomp6_output(struct sk_buff **pskb)
 {
        int err;
-       struct dst_entry *dst = skb->dst;
+       struct dst_entry *dst = (*pskb)->dst;
        struct xfrm_state *x = dst->xfrm;
        struct ipv6hdr *tmp_iph = NULL, *iph, *top_iph;
        int hdr_len = 0;
@@ -132,54 +132,55 @@ static int ipcomp6_output(struct sk_buff *skb)
        int plen, dlen;
        u8 *start, *scratch = ipcd->scratch;
 
-       if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
-               err = -EINVAL;
-               goto error_nolock;
+       if ((*pskb)->ip_summed == CHECKSUM_HW) {
+               err = skb_checksum_help(pskb, 0);
+               if (err)
+                       goto error_nolock;
        }
 
        spin_lock_bh(&x->lock);
 
-       err = xfrm_check_output(x, skb, AF_INET6);
+       err = xfrm_check_output(x, *pskb, AF_INET6);
        if (err)
                goto error;
 
        if (x->props.mode) {
                hdr_len = sizeof(struct ipv6hdr);
                nexthdr = IPPROTO_IPV6;
-               iph = skb->nh.ipv6h;
-               top_iph = (struct ipv6hdr *)skb_push(skb, sizeof(struct ipv6hdr));
+               iph = (*pskb)->nh.ipv6h;
+               top_iph = (struct ipv6hdr *)skb_push(*pskb, sizeof(struct ipv6hdr));
                top_iph->version = 6;
                top_iph->priority = iph->priority;
                top_iph->flow_lbl[0] = iph->flow_lbl[0];
                top_iph->flow_lbl[1] = iph->flow_lbl[1];
                top_iph->flow_lbl[2] = iph->flow_lbl[2];
                top_iph->nexthdr = IPPROTO_IPV6; /* initial */
-               top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
+               top_iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
                top_iph->hop_limit = iph->hop_limit;
                memcpy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr, sizeof(struct in6_addr));
                memcpy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr, sizeof(struct in6_addr));
-               skb->nh.raw = skb->data; /* == top_iph */
-               skb->h.raw = skb->nh.raw + hdr_len;
+               (*pskb)->nh.raw = (*pskb)->data; /* == top_iph */
+               (*pskb)->h.raw = (*pskb)->nh.raw + hdr_len;
        } else {
-               hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
+               hdr_len = ip6_find_1stfragopt(*pskb, &prevhdr);
                nexthdr = *prevhdr;
        }
 
        /* check whether datagram len is larger than threshold */
-       if ((skb->len - hdr_len) < ipcd->threshold) {
+       if (((*pskb)->len - hdr_len) < ipcd->threshold) {
                goto out_ok;
        }
 
-       if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
-               skb_linearize(skb, GFP_ATOMIC) != 0) {
+       if ((skb_is_nonlinear(*pskb) || skb_cloned(*pskb)) &&
+               skb_linearize(*pskb, GFP_ATOMIC) != 0) {
                err = -ENOMEM;
                goto error;
        }
 
        /* compression */
-       plen = skb->len - hdr_len;
+       plen = (*pskb)->len - hdr_len;
        dlen = IPCOMP_SCRATCH_SIZE;
-       start = skb->data + hdr_len;
+       start = (*pskb)->data + hdr_len;
 
        err = crypto_comp_compress(ipcd->tfm, start, plen, scratch, &dlen);
        if (err) {
@@ -189,7 +190,7 @@ static int ipcomp6_output(struct sk_buff *skb)
                goto out_ok;
        }
        memcpy(start, scratch, dlen);
-       pskb_trim(skb, hdr_len+dlen);
+       pskb_trim(*pskb, hdr_len+dlen);
 
        /* insert ipcomp header and replace datagram */
        tmp_iph = kmalloc(hdr_len, GFP_ATOMIC);
@@ -197,16 +198,16 @@ static int ipcomp6_output(struct sk_buff *skb)
                err = -ENOMEM;
                goto error;
        }
-       memcpy(tmp_iph, skb->nh.raw, hdr_len);
-       top_iph = (struct ipv6hdr*)skb_push(skb, sizeof(struct ipv6_comp_hdr));
+       memcpy(tmp_iph, (*pskb)->nh.raw, hdr_len);
+       top_iph = (struct ipv6hdr*)skb_push(*pskb, sizeof(struct ipv6_comp_hdr));
        memcpy(top_iph, tmp_iph, hdr_len);
        kfree(tmp_iph);
 
        if (x->props.mode && (x->props.flags & XFRM_STATE_NOECN))
                IP6_ECN_clear(top_iph);
-       top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
-       skb->nh.raw = skb->data; /* top_iph */
-       ip6_find_1stfragopt(skb, &prevhdr); 
+       top_iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
+       (*pskb)->nh.raw = (*pskb)->data; /* top_iph */
+       ip6_find_1stfragopt(*pskb, &prevhdr); 
        *prevhdr = IPPROTO_COMP;
 
        ipch = (struct ipv6_comp_hdr *)((unsigned char *)top_iph + hdr_len);
@@ -214,13 +215,13 @@ static int ipcomp6_output(struct sk_buff *skb)
        ipch->flags = 0;
        ipch->cpi = htons((u16 )ntohl(x->id.spi));
 
-       skb->h.raw = (unsigned char*)ipch;
+       (*pskb)->h.raw = (unsigned char*)ipch;
 out_ok:
-       x->curlft.bytes += skb->len;
+       x->curlft.bytes += (*pskb)->len;
        x->curlft.packets++;
        spin_unlock_bh(&x->lock);
 
-       if ((skb->dst = dst_pop(dst)) == NULL) {
+       if (((*pskb)->dst = dst_pop(dst)) == NULL) {
                err = -EHOSTUNREACH;
                goto error_nolock;
        }
@@ -231,7 +232,7 @@ out_exit:
 error:
        spin_unlock_bh(&x->lock);
 error_nolock:
-       kfree_skb(skb);
+       kfree_skb(*pskb);
        goto out_exit;
 }
 
index 18057ed15d568a5c384017db88802822af9880cf..e09d6fe82e684525798f585ffeda3f48379cb0a9 100644 (file)
@@ -87,6 +87,7 @@ static void           ip6_dst_destroy(struct dst_entry *);
 static int              ip6_dst_gc(void);
 
 static int             ip6_pkt_discard(struct sk_buff *skb);
+static int             ip6_pkt_discard_out(struct sk_buff **pskb);
 static void            ip6_link_failure(struct sk_buff *skb);
 static void            ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
 
@@ -113,7 +114,7 @@ struct rt6_info ip6_null_entry = {
                        .error          = -ENETUNREACH,
                        .metrics        = { [RTAX_HOPLIMIT - 1] = 255, },
                        .input          = ip6_pkt_discard,
-                       .output         = ip6_pkt_discard,
+                       .output         = ip6_pkt_discard_out,
                        .ops            = &ip6_dst_ops,
                        .path           = (struct dst_entry*)&ip6_null_entry,
                }
@@ -576,7 +577,7 @@ static struct dst_entry *ndisc_dst_gc_list;
 struct dst_entry *ndisc_dst_alloc(struct net_device *dev, 
                                  struct neighbour *neigh,
                                  struct in6_addr *addr,
-                                 int (*output)(struct sk_buff *))
+                                 int (*output)(struct sk_buff **))
 {
        struct rt6_info *rt = ip6_dst_alloc();
 
@@ -778,7 +779,7 @@ int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh, void *_rtattr)
                        dev_put(dev);
                dev = &loopback_dev;
                dev_hold(dev);
-               rt->u.dst.output = ip6_pkt_discard;
+               rt->u.dst.output = ip6_pkt_discard_out;
                rt->u.dst.input = ip6_pkt_discard;
                rt->u.dst.error = -ENETUNREACH;
                rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
@@ -1278,6 +1279,11 @@ int ip6_pkt_discard(struct sk_buff *skb)
        return 0;
 }
 
+int ip6_pkt_discard_out(struct sk_buff **pskb)
+{
+       return ip6_pkt_discard(*pskb);
+}
+
 /*
  *     Add address
  */