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

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [net/] [ipv6/] [ah6.c] - Blame information for rev 82

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
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/ah.c.
25
 */
26
 
27
#include <linux/module.h>
28
#include <net/ip.h>
29
#include <net/ah.h>
30
#include <linux/crypto.h>
31
#include <linux/pfkeyv2.h>
32
#include <linux/spinlock.h>
33
#include <linux/string.h>
34
#include <net/icmp.h>
35
#include <net/ipv6.h>
36
#include <net/protocol.h>
37
#include <net/xfrm.h>
38
 
39
static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr)
40
{
41
        u8 *opt = (u8 *)opthdr;
42
        int len = ipv6_optlen(opthdr);
43
        int off = 0;
44
        int optlen = 0;
45
 
46
        off += 2;
47
        len -= 2;
48
 
49
        while (len > 0) {
50
 
51
                switch (opt[off]) {
52
 
53
                case IPV6_TLV_PAD0:
54
                        optlen = 1;
55
                        break;
56
                default:
57
                        if (len < 2)
58
                                goto bad;
59
                        optlen = opt[off+1]+2;
60
                        if (len < optlen)
61
                                goto bad;
62
                        if (opt[off] & 0x20)
63
                                memset(&opt[off+2], 0, opt[off+1]);
64
                        break;
65
                }
66
 
67
                off += optlen;
68
                len -= optlen;
69
        }
70
        if (len == 0)
71
                return 1;
72
 
73
bad:
74
        return 0;
75
}
76
 
77
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
78
/**
79
 *      ipv6_rearrange_destopt - rearrange IPv6 destination options header
80
 *      @iph: IPv6 header
81
 *      @destopt: destionation options header
82
 */
83
static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt)
84
{
85
        u8 *opt = (u8 *)destopt;
86
        int len = ipv6_optlen(destopt);
87
        int off = 0;
88
        int optlen = 0;
89
 
90
        off += 2;
91
        len -= 2;
92
 
93
        while (len > 0) {
94
 
95
                switch (opt[off]) {
96
 
97
                case IPV6_TLV_PAD0:
98
                        optlen = 1;
99
                        break;
100
                default:
101
                        if (len < 2)
102
                                goto bad;
103
                        optlen = opt[off+1]+2;
104
                        if (len < optlen)
105
                                goto bad;
106
 
107
                        /* Rearrange the source address in @iph and the
108
                         * addresses in home address option for final source.
109
                         * See 11.3.2 of RFC 3775 for details.
110
                         */
111
                        if (opt[off] == IPV6_TLV_HAO) {
112
                                struct in6_addr final_addr;
113
                                struct ipv6_destopt_hao *hao;
114
 
115
                                hao = (struct ipv6_destopt_hao *)&opt[off];
116
                                if (hao->length != sizeof(hao->addr)) {
117
                                        if (net_ratelimit())
118
                                                printk(KERN_WARNING "destopt hao: invalid header length: %u\n", hao->length);
119
                                        goto bad;
120
                                }
121
                                ipv6_addr_copy(&final_addr, &hao->addr);
122
                                ipv6_addr_copy(&hao->addr, &iph->saddr);
123
                                ipv6_addr_copy(&iph->saddr, &final_addr);
124
                        }
125
                        break;
126
                }
127
 
128
                off += optlen;
129
                len -= optlen;
130
        }
131
        /* Note: ok if len == 0 */
132
bad:
133
        return;
134
}
135
#else
136
static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt) {}
137
#endif
138
 
139
/**
140
 *      ipv6_rearrange_rthdr - rearrange IPv6 routing header
141
 *      @iph: IPv6 header
142
 *      @rthdr: routing header
143
 *
144
 *      Rearrange the destination address in @iph and the addresses in @rthdr
145
 *      so that they appear in the order they will at the final destination.
146
 *      See Appendix A2 of RFC 2402 for details.
147
 */
148
static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
149
{
150
        int segments, segments_left;
151
        struct in6_addr *addrs;
152
        struct in6_addr final_addr;
153
 
154
        segments_left = rthdr->segments_left;
155
        if (segments_left == 0)
156
                return;
157
        rthdr->segments_left = 0;
158
 
159
        /* The value of rthdr->hdrlen has been verified either by the system
160
         * call if it is locally generated, or by ipv6_rthdr_rcv() for incoming
161
         * packets.  So we can assume that it is even and that segments is
162
         * greater than or equal to segments_left.
163
         *
164
         * For the same reason we can assume that this option is of type 0.
165
         */
166
        segments = rthdr->hdrlen >> 1;
167
 
168
        addrs = ((struct rt0_hdr *)rthdr)->addr;
169
        ipv6_addr_copy(&final_addr, addrs + segments - 1);
170
 
171
        addrs += segments - segments_left;
172
        memmove(addrs + 1, addrs, (segments_left - 1) * sizeof(*addrs));
173
 
174
        ipv6_addr_copy(addrs, &iph->daddr);
175
        ipv6_addr_copy(&iph->daddr, &final_addr);
176
}
177
 
178
static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
179
{
180
        union {
181
                struct ipv6hdr *iph;
182
                struct ipv6_opt_hdr *opth;
183
                struct ipv6_rt_hdr *rth;
184
                char *raw;
185
        } exthdr = { .iph = iph };
186
        char *end = exthdr.raw + len;
187
        int nexthdr = iph->nexthdr;
188
 
189
        exthdr.iph++;
190
 
191
        while (exthdr.raw < end) {
192
                switch (nexthdr) {
193
                case NEXTHDR_DEST:
194
                        if (dir == XFRM_POLICY_OUT)
195
                                ipv6_rearrange_destopt(iph, exthdr.opth);
196
                case NEXTHDR_HOP:
197
                        if (!zero_out_mutable_opts(exthdr.opth)) {
198
                                LIMIT_NETDEBUG(
199
                                        KERN_WARNING "overrun %sopts\n",
200
                                        nexthdr == NEXTHDR_HOP ?
201
                                                "hop" : "dest");
202
                                return -EINVAL;
203
                        }
204
                        break;
205
 
206
                case NEXTHDR_ROUTING:
207
                        ipv6_rearrange_rthdr(iph, exthdr.rth);
208
                        break;
209
 
210
                default :
211
                        return 0;
212
                }
213
 
214
                nexthdr = exthdr.opth->nexthdr;
215
                exthdr.raw += ipv6_optlen(exthdr.opth);
216
        }
217
 
218
        return 0;
219
}
220
 
221
static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
222
{
223
        int err;
224
        int extlen;
225
        struct ipv6hdr *top_iph;
226
        struct ip_auth_hdr *ah;
227
        struct ah_data *ahp;
228
        u8 nexthdr;
229
        char tmp_base[8];
230
        struct {
231
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
232
                struct in6_addr saddr;
233
#endif
234
                struct in6_addr daddr;
235
                char hdrs[0];
236
        } *tmp_ext;
237
 
238
        skb_push(skb, -skb_network_offset(skb));
239
        top_iph = ipv6_hdr(skb);
240
        top_iph->payload_len = htons(skb->len - sizeof(*top_iph));
241
 
242
        nexthdr = *skb_mac_header(skb);
243
        *skb_mac_header(skb) = IPPROTO_AH;
244
 
245
        /* When there are no extension headers, we only need to save the first
246
         * 8 bytes of the base IP header.
247
         */
248
        memcpy(tmp_base, top_iph, sizeof(tmp_base));
249
 
250
        tmp_ext = NULL;
251
        extlen = skb_transport_offset(skb) - sizeof(struct ipv6hdr);
252
        if (extlen) {
253
                extlen += sizeof(*tmp_ext);
254
                tmp_ext = kmalloc(extlen, GFP_ATOMIC);
255
                if (!tmp_ext) {
256
                        err = -ENOMEM;
257
                        goto error;
258
                }
259
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
260
                memcpy(tmp_ext, &top_iph->saddr, extlen);
261
#else
262
                memcpy(tmp_ext, &top_iph->daddr, extlen);
263
#endif
264
                err = ipv6_clear_mutable_options(top_iph,
265
                                                 extlen - sizeof(*tmp_ext) +
266
                                                 sizeof(*top_iph),
267
                                                 XFRM_POLICY_OUT);
268
                if (err)
269
                        goto error_free_iph;
270
        }
271
 
272
        ah = ip_auth_hdr(skb);
273
        ah->nexthdr = nexthdr;
274
 
275
        top_iph->priority    = 0;
276
        top_iph->flow_lbl[0] = 0;
277
        top_iph->flow_lbl[1] = 0;
278
        top_iph->flow_lbl[2] = 0;
279
        top_iph->hop_limit   = 0;
280
 
281
        ahp = x->data;
282
        ah->hdrlen  = (XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len) >> 2) - 2;
283
 
284
        ah->reserved = 0;
285
        ah->spi = x->id.spi;
286
        ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq);
287
 
288
        spin_lock_bh(&x->lock);
289
        err = ah_mac_digest(ahp, skb, ah->auth_data);
290
        memcpy(ah->auth_data, ahp->work_icv, ahp->icv_trunc_len);
291
        spin_unlock_bh(&x->lock);
292
 
293
        if (err)
294
                goto error_free_iph;
295
 
296
        memcpy(top_iph, tmp_base, sizeof(tmp_base));
297
        if (tmp_ext) {
298
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
299
                memcpy(&top_iph->saddr, tmp_ext, extlen);
300
#else
301
                memcpy(&top_iph->daddr, tmp_ext, extlen);
302
#endif
303
error_free_iph:
304
                kfree(tmp_ext);
305
        }
306
 
307
error:
308
        return err;
309
}
310
 
311
static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
312
{
313
        /*
314
         * Before process AH
315
         * [IPv6][Ext1][Ext2][AH][Dest][Payload]
316
         * |<-------------->| hdr_len
317
         *
318
         * To erase AH:
319
         * Keeping copy of cleared headers. After AH processing,
320
         * Moving the pointer of skb->network_header by using skb_pull as long
321
         * as AH header length. Then copy back the copy as long as hdr_len
322
         * If destination header following AH exists, copy it into after [Ext2].
323
         *
324
         * |<>|[IPv6][Ext1][Ext2][Dest][Payload]
325
         * There is offset of AH before IPv6 header after the process.
326
         */
327
 
328
        struct ip_auth_hdr *ah;
329
        struct ipv6hdr *ip6h;
330
        struct ah_data *ahp;
331
        unsigned char *tmp_hdr = NULL;
332
        u16 hdr_len;
333
        u16 ah_hlen;
334
        int nexthdr;
335
        int err = -EINVAL;
336
 
337
        if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr)))
338
                goto out;
339
 
340
        /* We are going to _remove_ AH header to keep sockets happy,
341
         * so... Later this can change. */
342
        if (skb_cloned(skb) &&
343
            pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
344
                goto out;
345
 
346
        skb->ip_summed = CHECKSUM_NONE;
347
 
348
        hdr_len = skb->data - skb_network_header(skb);
349
        ah = (struct ip_auth_hdr *)skb->data;
350
        ahp = x->data;
351
        nexthdr = ah->nexthdr;
352
        ah_hlen = (ah->hdrlen + 2) << 2;
353
 
354
        if (ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_full_len) &&
355
            ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len))
356
                goto out;
357
 
358
        if (!pskb_may_pull(skb, ah_hlen))
359
                goto out;
360
 
361
        tmp_hdr = kmemdup(skb_network_header(skb), hdr_len, GFP_ATOMIC);
362
        if (!tmp_hdr)
363
                goto out;
364
        ip6h = ipv6_hdr(skb);
365
        if (ipv6_clear_mutable_options(ip6h, hdr_len, XFRM_POLICY_IN))
366
                goto free_out;
367
        ip6h->priority    = 0;
368
        ip6h->flow_lbl[0] = 0;
369
        ip6h->flow_lbl[1] = 0;
370
        ip6h->flow_lbl[2] = 0;
371
        ip6h->hop_limit   = 0;
372
 
373
        {
374
                u8 auth_data[MAX_AH_AUTH_LEN];
375
 
376
                memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
377
                memset(ah->auth_data, 0, ahp->icv_trunc_len);
378
                skb_push(skb, hdr_len);
379
                err = ah_mac_digest(ahp, skb, ah->auth_data);
380
                if (err)
381
                        goto free_out;
382
                err = -EINVAL;
383
                if (memcmp(ahp->work_icv, auth_data, ahp->icv_trunc_len)) {
384
                        LIMIT_NETDEBUG(KERN_WARNING "ipsec ah authentication error\n");
385
                        x->stats.integrity_failed++;
386
                        goto free_out;
387
                }
388
        }
389
 
390
        skb->network_header += ah_hlen;
391
        memcpy(skb_network_header(skb), tmp_hdr, hdr_len);
392
        skb->transport_header = skb->network_header;
393
        __skb_pull(skb, ah_hlen + hdr_len);
394
 
395
        kfree(tmp_hdr);
396
 
397
        return nexthdr;
398
 
399
free_out:
400
        kfree(tmp_hdr);
401
out:
402
        return err;
403
}
404
 
405
static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
406
                    int type, int code, int offset, __be32 info)
407
{
408
        struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
409
        struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset);
410
        struct xfrm_state *x;
411
 
412
        if (type != ICMPV6_DEST_UNREACH &&
413
            type != ICMPV6_PKT_TOOBIG)
414
                return;
415
 
416
        x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6);
417
        if (!x)
418
                return;
419
 
420
        NETDEBUG(KERN_DEBUG "pmtu discovery on SA AH/%08x/" NIP6_FMT "\n",
421
                 ntohl(ah->spi), NIP6(iph->daddr));
422
 
423
        xfrm_state_put(x);
424
}
425
 
426
static int ah6_init_state(struct xfrm_state *x)
427
{
428
        struct ah_data *ahp = NULL;
429
        struct xfrm_algo_desc *aalg_desc;
430
        struct crypto_hash *tfm;
431
 
432
        if (!x->aalg)
433
                goto error;
434
 
435
        if (x->encap)
436
                goto error;
437
 
438
        ahp = kzalloc(sizeof(*ahp), GFP_KERNEL);
439
        if (ahp == NULL)
440
                return -ENOMEM;
441
 
442
        tfm = crypto_alloc_hash(x->aalg->alg_name, 0, CRYPTO_ALG_ASYNC);
443
        if (IS_ERR(tfm))
444
                goto error;
445
 
446
        ahp->tfm = tfm;
447
        if (crypto_hash_setkey(tfm, x->aalg->alg_key,
448
                               (x->aalg->alg_key_len + 7) / 8))
449
                goto error;
450
 
451
        /*
452
         * Lookup the algorithm description maintained by xfrm_algo,
453
         * verify crypto transform properties, and store information
454
         * we need for AH processing.  This lookup cannot fail here
455
         * after a successful crypto_alloc_hash().
456
         */
457
        aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
458
        BUG_ON(!aalg_desc);
459
 
460
        if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
461
            crypto_hash_digestsize(tfm)) {
462
                printk(KERN_INFO "AH: %s digestsize %u != %hu\n",
463
                       x->aalg->alg_name, crypto_hash_digestsize(tfm),
464
                       aalg_desc->uinfo.auth.icv_fullbits/8);
465
                goto error;
466
        }
467
 
468
        ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
469
        ahp->icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
470
 
471
        BUG_ON(ahp->icv_trunc_len > MAX_AH_AUTH_LEN);
472
 
473
        ahp->work_icv = kmalloc(ahp->icv_full_len, GFP_KERNEL);
474
        if (!ahp->work_icv)
475
                goto error;
476
 
477
        x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) +
478
                                          ahp->icv_trunc_len);
479
        switch (x->props.mode) {
480
        case XFRM_MODE_BEET:
481
        case XFRM_MODE_TRANSPORT:
482
                break;
483
        case XFRM_MODE_TUNNEL:
484
                x->props.header_len += sizeof(struct ipv6hdr);
485
                break;
486
        default:
487
                goto error;
488
        }
489
        x->data = ahp;
490
 
491
        return 0;
492
 
493
error:
494
        if (ahp) {
495
                kfree(ahp->work_icv);
496
                crypto_free_hash(ahp->tfm);
497
                kfree(ahp);
498
        }
499
        return -EINVAL;
500
}
501
 
502
static void ah6_destroy(struct xfrm_state *x)
503
{
504
        struct ah_data *ahp = x->data;
505
 
506
        if (!ahp)
507
                return;
508
 
509
        kfree(ahp->work_icv);
510
        ahp->work_icv = NULL;
511
        crypto_free_hash(ahp->tfm);
512
        ahp->tfm = NULL;
513
        kfree(ahp);
514
}
515
 
516
static struct xfrm_type ah6_type =
517
{
518
        .description    = "AH6",
519
        .owner          = THIS_MODULE,
520
        .proto          = IPPROTO_AH,
521
        .flags          = XFRM_TYPE_REPLAY_PROT,
522
        .init_state     = ah6_init_state,
523
        .destructor     = ah6_destroy,
524
        .input          = ah6_input,
525
        .output         = ah6_output,
526
        .hdr_offset     = xfrm6_find_1stfragopt,
527
};
528
 
529
static struct inet6_protocol ah6_protocol = {
530
        .handler        =       xfrm6_rcv,
531
        .err_handler    =       ah6_err,
532
        .flags          =       INET6_PROTO_NOPOLICY,
533
};
534
 
535
static int __init ah6_init(void)
536
{
537
        if (xfrm_register_type(&ah6_type, AF_INET6) < 0) {
538
                printk(KERN_INFO "ipv6 ah init: can't add xfrm type\n");
539
                return -EAGAIN;
540
        }
541
 
542
        if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) {
543
                printk(KERN_INFO "ipv6 ah init: can't add protocol\n");
544
                xfrm_unregister_type(&ah6_type, AF_INET6);
545
                return -EAGAIN;
546
        }
547
 
548
        return 0;
549
}
550
 
551
static void __exit ah6_fini(void)
552
{
553
        if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0)
554
                printk(KERN_INFO "ipv6 ah close: can't remove protocol\n");
555
 
556
        if (xfrm_unregister_type(&ah6_type, AF_INET6) < 0)
557
                printk(KERN_INFO "ipv6 ah close: can't remove xfrm type\n");
558
 
559
}
560
 
561
module_init(ah6_init);
562
module_exit(ah6_fini);
563
 
564
MODULE_LICENSE("GPL");
565
MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_AH);

powered by: WebSVN 2.1.0

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