OpenCores
URL https://opencores.org/ocsvn/or1k_soc_on_altera_embedded_dev_kit/or1k_soc_on_altera_embedded_dev_kit/trunk

Subversion Repositories or1k_soc_on_altera_embedded_dev_kit

[/] [or1k_soc_on_altera_embedded_dev_kit/] [trunk/] [linux-2.6/] [linux-2.6.24/] [net/] [ipv6/] [esp6.c] - Blame information for rev 3

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 3 xianfeng
/*
2
 * Copyright (C)2002 USAGI/WIDE Project
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 2 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software
16
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
 *
18
 * Authors
19
 *
20
 *      Mitsuru KANDA @USAGI       : IPv6 Support
21
 *      Kazunori MIYAZAWA @USAGI   :
22
 *      Kunihiro Ishiguro <kunihiro@ipinfusion.com>
23
 *
24
 *      This file is derived from net/ipv4/esp.c
25
 */
26
 
27
#include <linux/err.h>
28
#include <linux/module.h>
29
#include <net/ip.h>
30
#include <net/xfrm.h>
31
#include <net/esp.h>
32
#include <linux/scatterlist.h>
33
#include <linux/crypto.h>
34
#include <linux/kernel.h>
35
#include <linux/pfkeyv2.h>
36
#include <linux/random.h>
37
#include <linux/spinlock.h>
38
#include <net/icmp.h>
39
#include <net/ipv6.h>
40
#include <net/protocol.h>
41
#include <linux/icmpv6.h>
42
 
43
static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
44
{
45
        int err;
46
        struct ip_esp_hdr *esph;
47
        struct crypto_blkcipher *tfm;
48
        struct blkcipher_desc desc;
49
        struct sk_buff *trailer;
50
        int blksize;
51
        int clen;
52
        int alen;
53
        int nfrags;
54
        u8 *tail;
55
        struct esp_data *esp = x->data;
56
 
57
        /* skb is pure payload to encrypt */
58
        err = -ENOMEM;
59
 
60
        /* Round to block size */
61
        clen = skb->len;
62
 
63
        alen = esp->auth.icv_trunc_len;
64
        tfm = esp->conf.tfm;
65
        desc.tfm = tfm;
66
        desc.flags = 0;
67
        blksize = ALIGN(crypto_blkcipher_blocksize(tfm), 4);
68
        clen = ALIGN(clen + 2, blksize);
69
        if (esp->conf.padlen)
70
                clen = ALIGN(clen, esp->conf.padlen);
71
 
72
        if ((nfrags = skb_cow_data(skb, clen-skb->len+alen, &trailer)) < 0) {
73
                goto error;
74
        }
75
 
76
        /* Fill padding... */
77
        tail = skb_tail_pointer(trailer);
78
        do {
79
                int i;
80
                for (i=0; i<clen-skb->len - 2; i++)
81
                        tail[i] = i + 1;
82
        } while (0);
83
        tail[clen-skb->len - 2] = (clen - skb->len) - 2;
84
        pskb_put(skb, trailer, clen - skb->len);
85
 
86
        skb_push(skb, -skb_network_offset(skb));
87
        esph = ip_esp_hdr(skb);
88
        *(skb_tail_pointer(trailer) - 1) = *skb_mac_header(skb);
89
        *skb_mac_header(skb) = IPPROTO_ESP;
90
 
91
        esph->spi = x->id.spi;
92
        esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq);
93
 
94
        spin_lock_bh(&x->lock);
95
 
96
        if (esp->conf.ivlen) {
97
                if (unlikely(!esp->conf.ivinitted)) {
98
                        get_random_bytes(esp->conf.ivec, esp->conf.ivlen);
99
                        esp->conf.ivinitted = 1;
100
                }
101
                crypto_blkcipher_set_iv(tfm, esp->conf.ivec, esp->conf.ivlen);
102
        }
103
 
104
        do {
105
                struct scatterlist *sg = &esp->sgbuf[0];
106
 
107
                if (unlikely(nfrags > ESP_NUM_FAST_SG)) {
108
                        sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC);
109
                        if (!sg)
110
                                goto unlock;
111
                }
112
                sg_init_table(sg, nfrags);
113
                skb_to_sgvec(skb, sg,
114
                             esph->enc_data +
115
                             esp->conf.ivlen -
116
                             skb->data, clen);
117
                err = crypto_blkcipher_encrypt(&desc, sg, sg, clen);
118
                if (unlikely(sg != &esp->sgbuf[0]))
119
                        kfree(sg);
120
        } while (0);
121
 
122
        if (unlikely(err))
123
                goto unlock;
124
 
125
        if (esp->conf.ivlen) {
126
                memcpy(esph->enc_data, esp->conf.ivec, esp->conf.ivlen);
127
                crypto_blkcipher_get_iv(tfm, esp->conf.ivec, esp->conf.ivlen);
128
        }
129
 
130
        if (esp->auth.icv_full_len) {
131
                err = esp_mac_digest(esp, skb, (u8 *)esph - skb->data,
132
                                     sizeof(*esph) + esp->conf.ivlen + clen);
133
                memcpy(pskb_put(skb, trailer, alen), esp->auth.work_icv, alen);
134
        }
135
 
136
unlock:
137
        spin_unlock_bh(&x->lock);
138
 
139
error:
140
        return err;
141
}
142
 
143
static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
144
{
145
        struct ipv6hdr *iph;
146
        struct ip_esp_hdr *esph;
147
        struct esp_data *esp = x->data;
148
        struct crypto_blkcipher *tfm = esp->conf.tfm;
149
        struct blkcipher_desc desc = { .tfm = tfm };
150
        struct sk_buff *trailer;
151
        int blksize = ALIGN(crypto_blkcipher_blocksize(tfm), 4);
152
        int alen = esp->auth.icv_trunc_len;
153
        int elen = skb->len - sizeof(*esph) - esp->conf.ivlen - alen;
154
        int hdr_len = skb_network_header_len(skb);
155
        int nfrags;
156
        int ret = 0;
157
 
158
        if (!pskb_may_pull(skb, sizeof(*esph))) {
159
                ret = -EINVAL;
160
                goto out;
161
        }
162
 
163
        if (elen <= 0 || (elen & (blksize-1))) {
164
                ret = -EINVAL;
165
                goto out;
166
        }
167
 
168
        /* If integrity check is required, do this. */
169
        if (esp->auth.icv_full_len) {
170
                u8 sum[alen];
171
 
172
                ret = esp_mac_digest(esp, skb, 0, skb->len - alen);
173
                if (ret)
174
                        goto out;
175
 
176
                if (skb_copy_bits(skb, skb->len - alen, sum, alen))
177
                        BUG();
178
 
179
                if (unlikely(memcmp(esp->auth.work_icv, sum, alen))) {
180
                        x->stats.integrity_failed++;
181
                        ret = -EINVAL;
182
                        goto out;
183
                }
184
        }
185
 
186
        if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0) {
187
                ret = -EINVAL;
188
                goto out;
189
        }
190
 
191
        skb->ip_summed = CHECKSUM_NONE;
192
 
193
        esph = (struct ip_esp_hdr *)skb->data;
194
        iph = ipv6_hdr(skb);
195
 
196
        /* Get ivec. This can be wrong, check against another impls. */
197
        if (esp->conf.ivlen)
198
                crypto_blkcipher_set_iv(tfm, esph->enc_data, esp->conf.ivlen);
199
 
200
        {
201
                u8 nexthdr[2];
202
                struct scatterlist *sg = &esp->sgbuf[0];
203
                u8 padlen;
204
 
205
                if (unlikely(nfrags > ESP_NUM_FAST_SG)) {
206
                        sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC);
207
                        if (!sg) {
208
                                ret = -ENOMEM;
209
                                goto out;
210
                        }
211
                }
212
                sg_init_table(sg, nfrags);
213
                skb_to_sgvec(skb, sg,
214
                             sizeof(*esph) + esp->conf.ivlen,
215
                             elen);
216
                ret = crypto_blkcipher_decrypt(&desc, sg, sg, elen);
217
                if (unlikely(sg != &esp->sgbuf[0]))
218
                        kfree(sg);
219
                if (unlikely(ret))
220
                        goto out;
221
 
222
                if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2))
223
                        BUG();
224
 
225
                padlen = nexthdr[0];
226
                if (padlen+2 >= elen) {
227
                        LIMIT_NETDEBUG(KERN_WARNING "ipsec esp packet is garbage padlen=%d, elen=%d\n", padlen+2, elen);
228
                        ret = -EINVAL;
229
                        goto out;
230
                }
231
                /* ... check padding bits here. Silly. :-) */
232
 
233
                /* RFC4303: Drop dummy packets without any error */
234
                if (nexthdr[1] == IPPROTO_NONE) {
235
                        ret = -EINVAL;
236
                        goto out;
237
                }
238
 
239
                pskb_trim(skb, skb->len - alen - padlen - 2);
240
                ret = nexthdr[1];
241
        }
242
 
243
        __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen);
244
        skb_set_transport_header(skb, -hdr_len);
245
out:
246
        return ret;
247
}
248
 
249
static u32 esp6_get_mtu(struct xfrm_state *x, int mtu)
250
{
251
        struct esp_data *esp = x->data;
252
        u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4);
253
        u32 align = max_t(u32, blksize, esp->conf.padlen);
254
        u32 rem;
255
 
256
        mtu -= x->props.header_len + esp->auth.icv_trunc_len;
257
        rem = mtu & (align - 1);
258
        mtu &= ~(align - 1);
259
 
260
        if (x->props.mode != XFRM_MODE_TUNNEL) {
261
                u32 padsize = ((blksize - 1) & 7) + 1;
262
                mtu -= blksize - padsize;
263
                mtu += min_t(u32, blksize - padsize, rem);
264
        }
265
 
266
        return mtu - 2;
267
}
268
 
269
static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
270
                     int type, int code, int offset, __be32 info)
271
{
272
        struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
273
        struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data + offset);
274
        struct xfrm_state *x;
275
 
276
        if (type != ICMPV6_DEST_UNREACH &&
277
            type != ICMPV6_PKT_TOOBIG)
278
                return;
279
 
280
        x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET6);
281
        if (!x)
282
                return;
283
        printk(KERN_DEBUG "pmtu discovery on SA ESP/%08x/" NIP6_FMT "\n",
284
                        ntohl(esph->spi), NIP6(iph->daddr));
285
        xfrm_state_put(x);
286
}
287
 
288
static void esp6_destroy(struct xfrm_state *x)
289
{
290
        struct esp_data *esp = x->data;
291
 
292
        if (!esp)
293
                return;
294
 
295
        crypto_free_blkcipher(esp->conf.tfm);
296
        esp->conf.tfm = NULL;
297
        kfree(esp->conf.ivec);
298
        esp->conf.ivec = NULL;
299
        crypto_free_hash(esp->auth.tfm);
300
        esp->auth.tfm = NULL;
301
        kfree(esp->auth.work_icv);
302
        esp->auth.work_icv = NULL;
303
        kfree(esp);
304
}
305
 
306
static int esp6_init_state(struct xfrm_state *x)
307
{
308
        struct esp_data *esp = NULL;
309
        struct crypto_blkcipher *tfm;
310
 
311
        if (x->ealg == NULL)
312
                goto error;
313
 
314
        if (x->encap)
315
                goto error;
316
 
317
        esp = kzalloc(sizeof(*esp), GFP_KERNEL);
318
        if (esp == NULL)
319
                return -ENOMEM;
320
 
321
        if (x->aalg) {
322
                struct xfrm_algo_desc *aalg_desc;
323
                struct crypto_hash *hash;
324
 
325
                hash = crypto_alloc_hash(x->aalg->alg_name, 0,
326
                                         CRYPTO_ALG_ASYNC);
327
                if (IS_ERR(hash))
328
                        goto error;
329
 
330
                esp->auth.tfm = hash;
331
                if (crypto_hash_setkey(hash, x->aalg->alg_key,
332
                                       (x->aalg->alg_key_len + 7) / 8))
333
                        goto error;
334
 
335
                aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
336
                BUG_ON(!aalg_desc);
337
 
338
                if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
339
                    crypto_hash_digestsize(hash)) {
340
                        NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n",
341
                                 x->aalg->alg_name,
342
                                 crypto_hash_digestsize(hash),
343
                                 aalg_desc->uinfo.auth.icv_fullbits/8);
344
                        goto error;
345
                }
346
 
347
                esp->auth.icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
348
                esp->auth.icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
349
 
350
                esp->auth.work_icv = kmalloc(esp->auth.icv_full_len, GFP_KERNEL);
351
                if (!esp->auth.work_icv)
352
                        goto error;
353
        }
354
        tfm = crypto_alloc_blkcipher(x->ealg->alg_name, 0, CRYPTO_ALG_ASYNC);
355
        if (IS_ERR(tfm))
356
                goto error;
357
        esp->conf.tfm = tfm;
358
        esp->conf.ivlen = crypto_blkcipher_ivsize(tfm);
359
        esp->conf.padlen = 0;
360
        if (esp->conf.ivlen) {
361
                esp->conf.ivec = kmalloc(esp->conf.ivlen, GFP_KERNEL);
362
                if (unlikely(esp->conf.ivec == NULL))
363
                        goto error;
364
                esp->conf.ivinitted = 0;
365
        }
366
        if (crypto_blkcipher_setkey(tfm, x->ealg->alg_key,
367
                                    (x->ealg->alg_key_len + 7) / 8))
368
                goto error;
369
        x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
370
        switch (x->props.mode) {
371
        case XFRM_MODE_BEET:
372
        case XFRM_MODE_TRANSPORT:
373
                break;
374
        case XFRM_MODE_TUNNEL:
375
                x->props.header_len += sizeof(struct ipv6hdr);
376
                break;
377
        default:
378
                goto error;
379
        }
380
        x->data = esp;
381
        return 0;
382
 
383
error:
384
        x->data = esp;
385
        esp6_destroy(x);
386
        x->data = NULL;
387
        return -EINVAL;
388
}
389
 
390
static struct xfrm_type esp6_type =
391
{
392
        .description    = "ESP6",
393
        .owner          = THIS_MODULE,
394
        .proto          = IPPROTO_ESP,
395
        .flags          = XFRM_TYPE_REPLAY_PROT,
396
        .init_state     = esp6_init_state,
397
        .destructor     = esp6_destroy,
398
        .get_mtu        = esp6_get_mtu,
399
        .input          = esp6_input,
400
        .output         = esp6_output,
401
        .hdr_offset     = xfrm6_find_1stfragopt,
402
};
403
 
404
static struct inet6_protocol esp6_protocol = {
405
        .handler        =       xfrm6_rcv,
406
        .err_handler    =       esp6_err,
407
        .flags          =       INET6_PROTO_NOPOLICY,
408
};
409
 
410
static int __init esp6_init(void)
411
{
412
        if (xfrm_register_type(&esp6_type, AF_INET6) < 0) {
413
                printk(KERN_INFO "ipv6 esp init: can't add xfrm type\n");
414
                return -EAGAIN;
415
        }
416
        if (inet6_add_protocol(&esp6_protocol, IPPROTO_ESP) < 0) {
417
                printk(KERN_INFO "ipv6 esp init: can't add protocol\n");
418
                xfrm_unregister_type(&esp6_type, AF_INET6);
419
                return -EAGAIN;
420
        }
421
 
422
        return 0;
423
}
424
 
425
static void __exit esp6_fini(void)
426
{
427
        if (inet6_del_protocol(&esp6_protocol, IPPROTO_ESP) < 0)
428
                printk(KERN_INFO "ipv6 esp close: can't remove protocol\n");
429
        if (xfrm_unregister_type(&esp6_type, AF_INET6) < 0)
430
                printk(KERN_INFO "ipv6 esp close: can't remove xfrm type\n");
431
}
432
 
433
module_init(esp6_init);
434
module_exit(esp6_fini);
435
 
436
MODULE_LICENSE("GPL");
437
MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_ESP);

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.