* 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 <linux/rtnetlink.h>
#include <net/icmp.h>
#include <net/ipv6.h>
+#include <net/protocol.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
+#include <linux/mutex.h>
struct ipcomp6_tfms {
struct list_head list;
- struct crypto_tfm **tfms;
+ struct crypto_comp **tfms;
int users;
};
-static DECLARE_MUTEX(ipcomp6_resource_sem);
+static DEFINE_MUTEX(ipcomp6_resource_mutex);
static void **ipcomp6_scratches;
static int ipcomp6_scratch_users;
static LIST_HEAD(ipcomp6_tfms_list);
-static int ipcomp6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
+static int ipcomp6_input(struct xfrm_state *x, struct sk_buff *skb)
{
- int err = 0;
- u8 nexthdr = 0;
- int hdr_len = skb->h.raw - skb->nh.raw;
- unsigned char *tmp_hdr = NULL;
+ int err = -ENOMEM;
struct ipv6hdr *iph;
+ struct ipv6_comp_hdr *ipch;
int plen, dlen;
struct ipcomp_data *ipcd = x->data;
u8 *start, *scratch;
- struct crypto_tfm *tfm;
+ struct crypto_comp *tfm;
int cpu;
- if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
- skb_linearize(skb, GFP_ATOMIC) != 0) {
- err = -ENOMEM;
+ if (skb_linearize_cow(skb))
goto out;
- }
skb->ip_summed = CHECKSUM_NONE;
/* Remove ipcomp header and decompress original payload */
iph = skb->nh.ipv6h;
- 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;
+ ipch = (void *)skb->data;
+ skb->h.raw = skb->nh.raw + sizeof(*ipch);
+ __skb_pull(skb, sizeof(*ipch));
/* decompression */
plen = skb->len;
goto out_put_cpu;
}
- skb_put(skb, dlen - plen);
+ skb->truesize += 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:
- if (tmp_hdr)
- kfree(tmp_hdr);
- if (err)
- goto error_out;
- return nexthdr;
-error_out:
return err;
}
struct ipcomp_data *ipcd = x->data;
int plen, dlen;
u8 *start, *scratch;
- struct crypto_tfm *tfm;
+ struct crypto_comp *tfm;
int cpu;
hdr_len = skb->h.raw - skb->data;
goto out_ok;
}
- if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
- skb_linearize(skb, GFP_ATOMIC) != 0) {
+ if (skb_linearize_cow(skb))
goto out_ok;
- }
/* compression */
plen = skb->len - hdr_len;
}
static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
- int type, int code, int offset, __u32 info)
+ int type, int code, int offset, __be32 info)
{
- u32 spi;
+ __be32 spi;
struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
struct ipv6_comp_hdr *ipcomph = (struct ipv6_comp_hdr*)(skb->data+offset);
struct xfrm_state *x;
if (type != ICMPV6_DEST_UNREACH && type != ICMPV6_PKT_TOOBIG)
return;
- spi = ntohl(ntohs(ipcomph->cpi));
+ spi = htonl(ntohs(ipcomph->cpi));
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, IPPROTO_COMP, AF_INET6);
if (!x)
return;
- printk(KERN_DEBUG "pmtu discovery on SA IPCOMP/%08x/"
- "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ printk(KERN_DEBUG "pmtu discovery on SA IPCOMP/%08x/" NIP6_FMT "\n",
spi, NIP6(iph->daddr));
xfrm_state_put(x);
}
static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
{
struct xfrm_state *t = NULL;
+ u8 mode = XFRM_MODE_TUNNEL;
t = xfrm_state_alloc();
if (!t)
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;
- t->props.mode = 1;
+ if (x->props.mode == XFRM_MODE_BEET)
+ mode = x->props.mode;
+ t->props.mode = mode;
memcpy(t->props.saddr.a6, x->props.saddr.a6, sizeof(struct in6_addr));
- t->type = xfrm_get_type(IPPROTO_IPV6, t->props.family);
- if (t->type == NULL)
+ if (xfrm_init_state(t))
goto error;
- if (t->type->init_state(t, NULL))
- goto error;
-
- t->km.state = XFRM_STATE_VALID;
atomic_set(&t->tunnel_users, 1);
out:
return t;
error:
+ t->km.state = XFRM_STATE_DEAD;
xfrm_state_put(t);
+ t = NULL;
goto out;
}
{
int err = 0;
struct xfrm_state *t = NULL;
- u32 spi;
+ __be32 spi;
spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&x->props.saddr);
if (spi)
if (!scratches)
return;
- for_each_cpu(i) {
+ for_each_possible_cpu(i) {
void *scratch = *per_cpu_ptr(scratches, i);
- if (scratch)
- vfree(scratch);
+
+ vfree(scratch);
}
free_percpu(scratches);
ipcomp6_scratches = scratches;
- for_each_cpu(i) {
+ for_each_possible_cpu(i) {
void *scratch = vmalloc(IPCOMP_SCRATCH_SIZE);
if (!scratch)
return NULL;
return scratches;
}
-static void ipcomp6_free_tfms(struct crypto_tfm **tfms)
+static void ipcomp6_free_tfms(struct crypto_comp **tfms)
{
struct ipcomp6_tfms *pos;
int cpu;
if (!tfms)
return;
- for_each_cpu(cpu) {
- struct crypto_tfm *tfm = *per_cpu_ptr(tfms, cpu);
- if (tfm)
- crypto_free_tfm(tfm);
+ for_each_possible_cpu(cpu) {
+ struct crypto_comp *tfm = *per_cpu_ptr(tfms, cpu);
+ crypto_free_comp(tfm);
}
free_percpu(tfms);
}
-static struct crypto_tfm **ipcomp6_alloc_tfms(const char *alg_name)
+static struct crypto_comp **ipcomp6_alloc_tfms(const char *alg_name)
{
struct ipcomp6_tfms *pos;
- struct crypto_tfm **tfms;
+ struct crypto_comp **tfms;
int cpu;
/* This can be any valid CPU ID so we don't need locking. */
- cpu = smp_processor_id();
+ cpu = raw_smp_processor_id();
list_for_each_entry(pos, &ipcomp6_tfms_list, list) {
- struct crypto_tfm *tfm;
+ struct crypto_comp *tfm;
tfms = pos->tfms;
tfm = *per_cpu_ptr(tfms, cpu);
- if (!strcmp(crypto_tfm_alg_name(tfm), alg_name)) {
+ if (!strcmp(crypto_comp_name(tfm), alg_name)) {
pos->users++;
return tfms;
}
INIT_LIST_HEAD(&pos->list);
list_add(&pos->list, &ipcomp6_tfms_list);
- pos->tfms = tfms = alloc_percpu(struct crypto_tfm *);
+ pos->tfms = tfms = alloc_percpu(struct crypto_comp *);
if (!tfms)
goto error;
- for_each_cpu(cpu) {
- struct crypto_tfm *tfm = crypto_alloc_tfm(alg_name, 0);
+ for_each_possible_cpu(cpu) {
+ struct crypto_comp *tfm = crypto_alloc_comp(alg_name, 0,
+ CRYPTO_ALG_ASYNC);
if (!tfm)
goto error;
*per_cpu_ptr(tfms, cpu) = tfm;
if (!ipcd)
return;
xfrm_state_delete_tunnel(x);
- down(&ipcomp6_resource_sem);
+ mutex_lock(&ipcomp6_resource_mutex);
ipcomp6_free_data(ipcd);
- up(&ipcomp6_resource_sem);
+ mutex_unlock(&ipcomp6_resource_mutex);
kfree(ipcd);
xfrm6_tunnel_free_spi((xfrm_address_t *)&x->props.saddr);
}
-static int ipcomp6_init_state(struct xfrm_state *x, void *args)
+static int ipcomp6_init_state(struct xfrm_state *x)
{
int err;
struct ipcomp_data *ipcd;
goto out;
err = -ENOMEM;
- ipcd = kmalloc(sizeof(*ipcd), GFP_KERNEL);
+ ipcd = kzalloc(sizeof(*ipcd), GFP_KERNEL);
if (!ipcd)
goto out;
- memset(ipcd, 0, sizeof(*ipcd));
x->props.header_len = 0;
- if (x->props.mode)
+ if (x->props.mode == XFRM_MODE_TUNNEL)
x->props.header_len += sizeof(struct ipv6hdr);
- down(&ipcomp6_resource_sem);
+ mutex_lock(&ipcomp6_resource_mutex);
if (!ipcomp6_alloc_scratches())
goto error;
ipcd->tfms = ipcomp6_alloc_tfms(x->calg->alg_name);
if (!ipcd->tfms)
goto error;
- up(&ipcomp6_resource_sem);
+ mutex_unlock(&ipcomp6_resource_mutex);
- if (x->props.mode) {
+ if (x->props.mode == XFRM_MODE_TUNNEL) {
err = ipcomp6_tunnel_attach(x);
if (err)
goto error_tunnel;
out:
return err;
error_tunnel:
- down(&ipcomp6_resource_sem);
+ mutex_lock(&ipcomp6_resource_mutex);
error:
ipcomp6_free_data(ipcd);
- up(&ipcomp6_resource_sem);
+ mutex_unlock(&ipcomp6_resource_mutex);
kfree(ipcd);
goto out;
.destructor = ipcomp6_destroy,
.input = ipcomp6_input,
.output = ipcomp6_output,
+ .hdr_offset = xfrm6_find_1stfragopt,
};
static struct inet6_protocol ipcomp6_protocol =