Commit b0f17170 authored by James Morris's avatar James Morris

[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: default avatarJames Morris <jmorris@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@redhat.com>
parent 5a1c7700
......@@ -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;
......
......@@ -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;
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
};
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -100,15 +100,15 @@ static void dst_run_gc(unsigned long dummy)
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;
}
}
}
......
......@@ -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();
......
......@@ -684,8 +684,9 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type
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 @@ static int dn_route_input_slow(struct sk_buff *skb)
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;
......
......@@ -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;
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;
}
......
......@@ -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;
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;
}
......
......@@ -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) &&
......
......@@ -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;
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 @@ static int ipcomp_output(struct sk_buff *skb)
error:
spin_unlock_bh(&x->lock);
error_nolock:
kfree_skb(skb);
kfree_skb(*pskb);
goto out_exit;
}
......
......@@ -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:
......
......@@ -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
......
......@@ -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;
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;
......
......@@ -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;
}
......
......@@ -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 : "?");
......
......@@ -33,8 +33,9 @@ int xfrm4_tunnel_check_size(struct sk_buff *skb)
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;
......
......@@ -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;
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 @@ int ah6_output(struct sk_buff *skb)
error:
spin_unlock_bh(&x->lock);
error_nolock:
kfree_skb(skb);
kfree_skb(*pskb);
return err;
}
......
......@@ -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;
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;
}
......
......@@ -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,10 +607,12 @@ 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;
......@@ -721,7 +726,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
IP6_INC_STATS(FragCreates);
err = output(frag);
err = output(&frag);
if (err)
goto fail;
}
......
......@@ -118,10 +118,10 @@ static int ipcomp6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, s
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;
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 @@ static int ipcomp6_output(struct sk_buff *skb)
error:
spin_unlock_bh(&x->lock);
error_nolock:
kfree_skb(skb);
kfree_skb(*pskb);
goto out_exit;
}
......
......@@ -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
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment