* The decompression of IP datagram MUST be done after the reassembly,
* AH/ESP processing.
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <net/ip.h>
#include <net/xfrm.h>
#include <net/protocol.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
-#include <linux/mutex.h>
struct ipcomp6_tfms {
struct list_head list;
int users;
};
-static DEFINE_MUTEX(ipcomp6_resource_mutex);
+static DECLARE_MUTEX(ipcomp6_resource_sem);
static void **ipcomp6_scratches;
static int ipcomp6_scratch_users;
static LIST_HEAD(ipcomp6_tfms_list);
-static int ipcomp6_input(struct xfrm_state *x, struct sk_buff *skb)
+static int ipcomp6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
{
- int err = -ENOMEM;
+ int err = 0;
+ u8 nexthdr = 0;
+ int hdr_len = skb->h.raw - skb->nh.raw;
+ unsigned char *tmp_hdr = NULL;
struct ipv6hdr *iph;
- struct ipv6_comp_hdr *ipch;
int plen, dlen;
struct ipcomp_data *ipcd = x->data;
u8 *start, *scratch;
struct crypto_tfm *tfm;
int cpu;
- if (skb_linearize_cow(skb))
+ if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
+ skb_linearize(skb, GFP_ATOMIC) != 0) {
+ err = -ENOMEM;
goto out;
+ }
skb->ip_summed = CHECKSUM_NONE;
/* Remove ipcomp header and decompress original payload */
iph = skb->nh.ipv6h;
- ipch = (void *)skb->data;
- skb->h.raw = skb->nh.raw + sizeof(*ipch);
- __skb_pull(skb, sizeof(*ipch));
+ tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
+ if (!tmp_hdr)
+ goto out;
+ memcpy(tmp_hdr, iph, hdr_len);
+ nexthdr = *(u8 *)skb->data;
+ skb_pull(skb, sizeof(struct ipv6_comp_hdr));
+ skb->nh.raw += sizeof(struct ipv6_comp_hdr);
+ memcpy(skb->nh.raw, tmp_hdr, hdr_len);
+ iph = skb->nh.ipv6h;
+ iph->payload_len = htons(ntohs(iph->payload_len) - sizeof(struct ipv6_comp_hdr));
+ skb->h.raw = skb->data;
/* decompression */
plen = skb->len;
goto out_put_cpu;
}
- skb->truesize += dlen - plen;
- __skb_put(skb, dlen - plen);
+ skb_put(skb, dlen - plen);
memcpy(skb->data, scratch, dlen);
- err = ipch->nexthdr;
+ iph = skb->nh.ipv6h;
+ iph->payload_len = htons(skb->len);
+
out_put_cpu:
put_cpu();
out:
+ kfree(tmp_hdr);
+ if (err)
+ goto error_out;
+ return nexthdr;
+error_out:
return err;
}
goto out_ok;
}
- if (skb_linearize_cow(skb))
+ if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
+ skb_linearize(skb, GFP_ATOMIC) != 0) {
goto out_ok;
+ }
/* compression */
plen = skb->len - hdr_len;
if (type != ICMPV6_DEST_UNREACH && type != ICMPV6_PKT_TOOBIG)
return;
- spi = htonl(ntohs(ipcomph->cpi));
+ spi = ntohl(ntohs(ipcomph->cpi));
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, IPPROTO_COMP, AF_INET6);
if (!x)
return;
t->id.proto = IPPROTO_IPV6;
t->id.spi = xfrm6_tunnel_alloc_spi((xfrm_address_t *)&x->props.saddr);
- if (!t->id.spi)
- goto error;
-
memcpy(t->id.daddr.a6, x->id.daddr.a6, sizeof(struct in6_addr));
memcpy(&t->sel, &x->sel, sizeof(t->sel));
t->props.family = AF_INET6;
return t;
error:
- t->km.state = XFRM_STATE_DEAD;
xfrm_state_put(t);
- t = NULL;
goto out;
}
if (!scratches)
return;
- for_each_possible_cpu(i) {
+ for_each_cpu(i) {
void *scratch = *per_cpu_ptr(scratches, i);
-
- vfree(scratch);
+ if (scratch)
+ vfree(scratch);
}
free_percpu(scratches);
ipcomp6_scratches = scratches;
- for_each_possible_cpu(i) {
+ for_each_cpu(i) {
void *scratch = vmalloc(IPCOMP_SCRATCH_SIZE);
if (!scratch)
return NULL;
if (!tfms)
return;
- for_each_possible_cpu(cpu) {
+ for_each_cpu(cpu) {
struct crypto_tfm *tfm = *per_cpu_ptr(tfms, cpu);
crypto_free_tfm(tfm);
}
if (!tfms)
goto error;
- for_each_possible_cpu(cpu) {
+ for_each_cpu(cpu) {
struct crypto_tfm *tfm = crypto_alloc_tfm(alg_name, 0);
if (!tfm)
goto error;
if (!ipcd)
return;
xfrm_state_delete_tunnel(x);
- mutex_lock(&ipcomp6_resource_mutex);
+ down(&ipcomp6_resource_sem);
ipcomp6_free_data(ipcd);
- mutex_unlock(&ipcomp6_resource_mutex);
+ up(&ipcomp6_resource_sem);
kfree(ipcd);
xfrm6_tunnel_free_spi((xfrm_address_t *)&x->props.saddr);
goto out;
err = -ENOMEM;
- ipcd = kzalloc(sizeof(*ipcd), GFP_KERNEL);
+ ipcd = kmalloc(sizeof(*ipcd), GFP_KERNEL);
if (!ipcd)
goto out;
+ memset(ipcd, 0, sizeof(*ipcd));
x->props.header_len = 0;
if (x->props.mode)
x->props.header_len += sizeof(struct ipv6hdr);
- mutex_lock(&ipcomp6_resource_mutex);
+ down(&ipcomp6_resource_sem);
if (!ipcomp6_alloc_scratches())
goto error;
ipcd->tfms = ipcomp6_alloc_tfms(x->calg->alg_name);
if (!ipcd->tfms)
goto error;
- mutex_unlock(&ipcomp6_resource_mutex);
+ up(&ipcomp6_resource_sem);
if (x->props.mode) {
err = ipcomp6_tunnel_attach(x);
out:
return err;
error_tunnel:
- mutex_lock(&ipcomp6_resource_mutex);
+ down(&ipcomp6_resource_sem);
error:
ipcomp6_free_data(ipcd);
- mutex_unlock(&ipcomp6_resource_mutex);
+ up(&ipcomp6_resource_sem);
kfree(ipcd);
goto out;