[IPSEC]: IPv4 over IPv6 IPsec tunnel
[linux-2.6] / net / ipv4 / xfrm4_mode_beet.c
1 /*
2  * xfrm4_mode_beet.c - BEET mode encapsulation for IPv4.
3  *
4  * Copyright (c) 2006 Diego Beltrami <diego.beltrami@gmail.com>
5  *                    Miika Komu     <miika@iki.fi>
6  *                    Herbert Xu     <herbert@gondor.apana.org.au>
7  *                    Abhinav Pathak <abhinav.pathak@hiit.fi>
8  *                    Jeff Ahrenholz <ahrenholz@gmail.com>
9  */
10
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/skbuff.h>
15 #include <linux/stringify.h>
16 #include <net/dst.h>
17 #include <net/ip.h>
18 #include <net/xfrm.h>
19
20 /* Add encapsulation header.
21  *
22  * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt.
23  * The following fields in it shall be filled in by x->type->output:
24  *      tot_len
25  *      check
26  *
27  * On exit, skb->h will be set to the start of the payload to be processed
28  * by x->type->output and skb->nh will be set to the top IP header.
29  */
30 static int xfrm4_beet_output(struct xfrm_state *x, struct sk_buff *skb)
31 {
32         struct iphdr *iph, *top_iph = NULL;
33         int hdrlen, optlen;
34
35         iph = skb->nh.iph;
36         skb->h.ipiph = iph;
37
38         hdrlen = 0;
39         optlen = iph->ihl * 4 - sizeof(*iph);
40         if (unlikely(optlen))
41                 hdrlen += IPV4_BEET_PHMAXLEN - (optlen & 4);
42
43         skb->nh.raw = skb_push(skb, x->props.header_len + hdrlen);
44         top_iph = skb->nh.iph;
45         hdrlen = iph->ihl * 4 - optlen;
46         skb->h.raw += hdrlen;
47
48         memmove(top_iph, iph, hdrlen);
49         if (unlikely(optlen)) {
50                 struct ip_beet_phdr *ph;
51
52                 BUG_ON(optlen < 0);
53
54                 ph = (struct ip_beet_phdr *)skb->h.raw;
55                 ph->padlen = 4 - (optlen & 4);
56                 ph->hdrlen = (optlen + ph->padlen + sizeof(*ph)) / 8;
57                 ph->nexthdr = top_iph->protocol;
58
59                 top_iph->protocol = IPPROTO_BEETPH;
60                 top_iph->ihl = sizeof(struct iphdr) / 4;
61         }
62
63         top_iph->saddr = x->props.saddr.a4;
64         top_iph->daddr = x->id.daddr.a4;
65
66         return 0;
67 }
68
69 static int xfrm4_beet_input(struct xfrm_state *x, struct sk_buff *skb)
70 {
71         struct iphdr *iph = skb->nh.iph;
72         int phlen = 0;
73         int optlen = 0;
74         __u8 ph_nexthdr = 0, protocol = 0;
75         int err = -EINVAL;
76
77         protocol = iph->protocol;
78
79         if (unlikely(iph->protocol == IPPROTO_BEETPH)) {
80                 struct ip_beet_phdr *ph = (struct ip_beet_phdr*)(iph + 1);
81
82                 if (!pskb_may_pull(skb, sizeof(*ph)))
83                         goto out;
84
85                 phlen = ph->hdrlen * 8;
86                 optlen = phlen - ph->padlen - sizeof(*ph);
87                 if (optlen < 0 || optlen & 3 || optlen > 250)
88                         goto out;
89
90                 if (!pskb_may_pull(skb, phlen))
91                         goto out;
92
93                 ph_nexthdr = ph->nexthdr;
94         }
95
96         skb_push(skb, sizeof(*iph) - phlen + optlen);
97         memmove(skb->data, skb->nh.raw, sizeof(*iph));
98         skb->nh.raw = skb->data;
99
100         iph = skb->nh.iph;
101         iph->ihl = (sizeof(*iph) + optlen) / 4;
102         iph->tot_len = htons(skb->len);
103         iph->daddr = x->sel.daddr.a4;
104         iph->saddr = x->sel.saddr.a4;
105         if (ph_nexthdr)
106                 iph->protocol = ph_nexthdr;
107         else
108                 iph->protocol = protocol;
109         iph->check = 0;
110         iph->check = ip_fast_csum(skb->nh.raw, iph->ihl);
111         err = 0;
112 out:
113         return err;
114 }
115
116 static struct xfrm_mode xfrm4_beet_mode = {
117         .input = xfrm4_beet_input,
118         .output = xfrm4_beet_output,
119         .owner = THIS_MODULE,
120         .encap = XFRM_MODE_BEET,
121 };
122
123 static int __init xfrm4_beet_init(void)
124 {
125         return xfrm_register_mode(&xfrm4_beet_mode, AF_INET);
126 }
127
128 static void __exit xfrm4_beet_exit(void)
129 {
130         int err;
131
132         err = xfrm_unregister_mode(&xfrm4_beet_mode, AF_INET);
133         BUG_ON(err);
134 }
135
136 module_init(xfrm4_beet_init);
137 module_exit(xfrm4_beet_exit);
138 MODULE_LICENSE("GPL");
139 MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_BEET);