-
-struct ipcomp_tfms {
- struct list_head list;
- struct crypto_tfm **tfms;
- int users;
-};
-
-static DECLARE_MUTEX(ipcomp_resource_sem);
-static void **ipcomp_scratches;
-static int ipcomp_scratch_users;
-static LIST_HEAD(ipcomp_tfms_list);
-
-static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
-{
- int err, plen, dlen;
- struct iphdr *iph;
- struct ipcomp_data *ipcd = x->data;
- u8 *start, *scratch;
- struct crypto_tfm *tfm;
- int cpu;
-
- plen = skb->len;
- dlen = IPCOMP_SCRATCH_SIZE;
- start = skb->data;
-
- cpu = get_cpu();
- scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
- tfm = *per_cpu_ptr(ipcd->tfms, cpu);
-
- err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen);
- if (err)
- goto out;
-
- if (dlen < (plen + sizeof(struct ip_comp_hdr))) {
- err = -EINVAL;
- goto out;
- }
-
- err = pskb_expand_head(skb, 0, dlen - plen, GFP_ATOMIC);
- if (err)
- goto out;
-
- skb_put(skb, dlen - plen);
- memcpy(skb->data, scratch, dlen);
- iph = skb->nh.iph;
- iph->tot_len = htons(dlen + iph->ihl * 4);
-out:
- put_cpu();
- return err;
-}
-
-static int ipcomp_input(struct xfrm_state *x,
- struct xfrm_decap_state *decap, struct sk_buff *skb)
-{
- u8 nexthdr;
- int err = 0;
- struct iphdr *iph;
- union {
- struct iphdr iph;
- char buf[60];
- } tmp_iph;
-
-
- 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.iph;
- memcpy(&tmp_iph, iph, iph->ihl * 4);
- nexthdr = *(u8 *)skb->data;
- skb_pull(skb, sizeof(struct ip_comp_hdr));
- skb->nh.raw += sizeof(struct ip_comp_hdr);
- memcpy(skb->nh.raw, &tmp_iph, tmp_iph.iph.ihl * 4);
- iph = skb->nh.iph;
- iph->tot_len = htons(ntohs(iph->tot_len) - sizeof(struct ip_comp_hdr));
- iph->protocol = nexthdr;
- skb->h.raw = skb->data;
- err = ipcomp_decompress(x, skb);
-
-out:
- return err;
-}
-
-static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
-{
- int err, plen, dlen, ihlen;
- struct iphdr *iph = skb->nh.iph;
- struct ipcomp_data *ipcd = x->data;
- u8 *start, *scratch;
- struct crypto_tfm *tfm;
- int cpu;
-
- ihlen = iph->ihl * 4;
- plen = skb->len - ihlen;
- dlen = IPCOMP_SCRATCH_SIZE;
- start = skb->data + ihlen;
-
- cpu = get_cpu();
- scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
- tfm = *per_cpu_ptr(ipcd->tfms, cpu);
-
- err = crypto_comp_compress(tfm, start, plen, scratch, &dlen);
- if (err)
- goto out;
-
- if ((dlen + sizeof(struct ip_comp_hdr)) >= plen) {
- err = -EMSGSIZE;
- goto out;
- }
-
- memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen);
- put_cpu();
-
- pskb_trim(skb, ihlen + dlen + sizeof(struct ip_comp_hdr));
- return 0;
-
-out:
- put_cpu();
- return err;
-}
-
-static int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb)
-{
- int err;
- struct iphdr *iph;
- struct ip_comp_hdr *ipch;
- struct ipcomp_data *ipcd = x->data;
- int hdr_len = 0;
-
- iph = skb->nh.iph;
- iph->tot_len = htons(skb->len);
- hdr_len = iph->ihl * 4;
- if ((skb->len - hdr_len) < ipcd->threshold) {
- /* Don't bother compressing */
- goto out_ok;
- }
-
- if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
- skb_linearize(skb, GFP_ATOMIC) != 0) {
- goto out_ok;
- }
-
- err = ipcomp_compress(x, skb);
- iph = skb->nh.iph;
-
- if (err) {
- goto out_ok;
- }
-
- /* Install ipcomp header, convert into ipcomp datagram. */
- iph->tot_len = htons(skb->len);
- ipch = (struct ip_comp_hdr *)((char *)iph + iph->ihl * 4);
- ipch->nexthdr = iph->protocol;
- ipch->flags = 0;
- ipch->cpi = htons((u16 )ntohl(x->id.spi));
- iph->protocol = IPPROTO_COMP;
- ip_send_check(iph);
- return 0;
-
-out_ok:
- if (x->props.mode)
- ip_send_check(iph);
- return 0;
-}