From b0f171707fba2fc73bc104a6a79964c4f0cfc46f Mon Sep 17 00:00:00 2001 From: James Morris Date: Thu, 3 Jun 2004 08:03:01 -0700 Subject: [PATCH] [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 Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- include/net/dst.h | 4 +- include/net/ip.h | 4 +- include/net/ip6_route.h | 2 +- include/net/ipv6.h | 4 +- include/net/xfrm.h | 2 +- net/bridge/br_netfilter.c | 2 +- net/core/dev.c | 49 ++++++++++----- net/core/dst.c | 22 +++---- net/core/netfilter.c | 8 --- net/decnet/dn_route.c | 10 ++- net/ipv4/ah4.c | 37 +++++------ net/ipv4/esp4.c | 58 +++++++++--------- net/ipv4/ip_output.c | 7 ++- net/ipv4/ipcomp.c | 45 +++++++------- net/ipv4/netfilter/ip_fw_compat.c | 3 +- net/ipv4/netfilter/ip_nat_standalone.c | 3 +- net/ipv4/netfilter/ipt_ECN.c | 14 +++-- net/ipv4/netfilter/ipt_TCPMSS.c | 4 +- net/ipv4/route.c | 4 +- net/ipv4/xfrm4_tunnel.c | 3 +- net/ipv6/ah6.c | 85 +++++++++++++------------- net/ipv6/esp6.c | 58 +++++++++--------- net/ipv6/ip6_output.c | 29 +++++---- net/ipv6/ipcomp6.c | 55 +++++++++-------- net/ipv6/route.c | 12 +++- 26 files changed, 285 insertions(+), 241 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d2c25ff410103..a55f97a340358 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -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; diff --git a/include/net/dst.h b/include/net/dst.h index 42dd8f511cefc..a7d83f545c28e 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -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; diff --git a/include/net/ip.h b/include/net/ip.h index 94ea1eda3b3dc..5a683ccd4cb03 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -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); diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index ea2781128829b..291149080a043 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -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); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 06b350cef868f..7b000ee2bef19 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -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); diff --git a/include/net/xfrm.h b/include/net/xfrm.h index e6d768a96be6e..64704968507f6 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -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); }; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 9651cdfa87cfd..4e91f1f00c62d 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -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; } diff --git a/net/core/dev.c b/net/core/dev.c index dfd505c1c5111..fb20e1047f276 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); diff --git a/net/core/dst.c b/net/core/dst.c index ba4856ccd57b6..1b0eaaa547fd6 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -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; } } } diff --git a/net/core/netfilter.c b/net/core/netfilter.c index dd8b9a8eefa8a..58632d189f52e 100644 --- a/net/core/netfilter.c +++ b/net/core/netfilter.c @@ -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(); diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index d9628dbef46ce..90d0583f56f42 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -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; diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index d3395b048e86e..0d62398f71a6b 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -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; } diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index d4696c1f01e38..4755e0dcc90d1 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -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; ilen - 2; i++) + for (i=0; ilen - 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; } diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index e9b7d78c8e1cd..60fc5091c0ef6 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -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) && diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c index 57d84fa777b4a..e20c7e43b2155 100644 --- a/net/ipv4/ipcomp.c +++ b/net/ipv4/ipcomp.c @@ -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; } diff --git a/net/ipv4/netfilter/ip_fw_compat.c b/net/ipv4/netfilter/ip_fw_compat.c index 0b26447c72d5a..f7146f31b6c3d 100644 --- a/net/ipv4/netfilter/ip_fw_compat.c +++ b/net/ipv4/netfilter/ip_fw_compat.c @@ -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: diff --git a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c index c4fe69b655250..a84453f899fe6 100644 --- a/net/ipv4/netfilter/ip_nat_standalone.c +++ b/net/ipv4/netfilter/ip_nat_standalone.c @@ -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 diff --git a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c index 341e845f4d4b6..8ca402564f5e0 100644 --- a/net/ipv4/netfilter/ipt_ECN.c +++ b/net/ipv4/netfilter/ipt_ECN.c @@ -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; diff --git a/net/ipv4/netfilter/ipt_TCPMSS.c b/net/ipv4/netfilter/ipt_TCPMSS.c index 369f84b4c0584..ab7a78f32b876 100644 --- a/net/ipv4/netfilter/ipt_TCPMSS.c +++ b/net/ipv4/netfilter/ipt_TCPMSS.c @@ -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; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6e7cbd378e8e2..03300f6a9f355 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -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 : "?"); diff --git a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c index accc0c8d6543b..3c9d4aaa21be3 100644 --- a/net/ipv4/xfrm4_tunnel.c +++ b/net/ipv4/xfrm4_tunnel.c @@ -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; diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 81c5d9766b7c8..6fde2c1c37f38 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -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; } diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index dc8bfac22d46d..164880299b404 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -40,11 +40,11 @@ #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; ilen - 2; i++) + for (i=0; ilen - 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; } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 87f0ef0eb817d..b9e7b26c5c29e 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -55,7 +55,7 @@ #include #include -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; } diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 97db913bf1b33..b357db4cc84bc 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -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; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 18057ed15d568..e09d6fe82e684 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -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 */ -- 2.53.0