~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to net/xfrm/xfrm_input.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * xfrm_input.c
 
3
 *
 
4
 * Changes:
 
5
 *      YOSHIFUJI Hideaki @USAGI
 
6
 *              Split up af-specific portion
 
7
 *
 
8
 */
 
9
 
 
10
#include <linux/slab.h>
 
11
#include <linux/module.h>
 
12
#include <linux/netdevice.h>
 
13
#include <net/dst.h>
 
14
#include <net/ip.h>
 
15
#include <net/xfrm.h>
 
16
 
 
17
static struct kmem_cache *secpath_cachep __read_mostly;
 
18
 
 
19
void __secpath_destroy(struct sec_path *sp)
 
20
{
 
21
        int i;
 
22
        for (i = 0; i < sp->len; i++)
 
23
                xfrm_state_put(sp->xvec[i]);
 
24
        kmem_cache_free(secpath_cachep, sp);
 
25
}
 
26
EXPORT_SYMBOL(__secpath_destroy);
 
27
 
 
28
struct sec_path *secpath_dup(struct sec_path *src)
 
29
{
 
30
        struct sec_path *sp;
 
31
 
 
32
        sp = kmem_cache_alloc(secpath_cachep, GFP_ATOMIC);
 
33
        if (!sp)
 
34
                return NULL;
 
35
 
 
36
        sp->len = 0;
 
37
        if (src) {
 
38
                int i;
 
39
 
 
40
                memcpy(sp, src, sizeof(*sp));
 
41
                for (i = 0; i < sp->len; i++)
 
42
                        xfrm_state_hold(sp->xvec[i]);
 
43
        }
 
44
        atomic_set(&sp->refcnt, 1);
 
45
        return sp;
 
46
}
 
47
EXPORT_SYMBOL(secpath_dup);
 
48
 
 
49
/* Fetch spi and seq from ipsec header */
 
50
 
 
51
int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
 
52
{
 
53
        int offset, offset_seq;
 
54
        int hlen;
 
55
 
 
56
        switch (nexthdr) {
 
57
        case IPPROTO_AH:
 
58
                hlen = sizeof(struct ip_auth_hdr);
 
59
                offset = offsetof(struct ip_auth_hdr, spi);
 
60
                offset_seq = offsetof(struct ip_auth_hdr, seq_no);
 
61
                break;
 
62
        case IPPROTO_ESP:
 
63
                hlen = sizeof(struct ip_esp_hdr);
 
64
                offset = offsetof(struct ip_esp_hdr, spi);
 
65
                offset_seq = offsetof(struct ip_esp_hdr, seq_no);
 
66
                break;
 
67
        case IPPROTO_COMP:
 
68
                if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
 
69
                        return -EINVAL;
 
70
                *spi = htonl(ntohs(*(__be16*)(skb_transport_header(skb) + 2)));
 
71
                *seq = 0;
 
72
                return 0;
 
73
        default:
 
74
                return 1;
 
75
        }
 
76
 
 
77
        if (!pskb_may_pull(skb, hlen))
 
78
                return -EINVAL;
 
79
 
 
80
        *spi = *(__be32*)(skb_transport_header(skb) + offset);
 
81
        *seq = *(__be32*)(skb_transport_header(skb) + offset_seq);
 
82
        return 0;
 
83
}
 
84
 
 
85
int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
 
86
{
 
87
        struct xfrm_mode *inner_mode = x->inner_mode;
 
88
        int err;
 
89
 
 
90
        err = x->outer_mode->afinfo->extract_input(x, skb);
 
91
        if (err)
 
92
                return err;
 
93
 
 
94
        if (x->sel.family == AF_UNSPEC) {
 
95
                inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
 
96
                if (inner_mode == NULL)
 
97
                        return -EAFNOSUPPORT;
 
98
        }
 
99
 
 
100
        skb->protocol = inner_mode->afinfo->eth_proto;
 
101
        return inner_mode->input2(x, skb);
 
102
}
 
103
EXPORT_SYMBOL(xfrm_prepare_input);
 
104
 
 
105
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
 
106
{
 
107
        struct net *net = dev_net(skb->dev);
 
108
        int err;
 
109
        __be32 seq;
 
110
        __be32 seq_hi;
 
111
        struct xfrm_state *x;
 
112
        xfrm_address_t *daddr;
 
113
        struct xfrm_mode *inner_mode;
 
114
        unsigned int family;
 
115
        int decaps = 0;
 
116
        int async = 0;
 
117
 
 
118
        /* A negative encap_type indicates async resumption. */
 
119
        if (encap_type < 0) {
 
120
                async = 1;
 
121
                x = xfrm_input_state(skb);
 
122
                seq = XFRM_SKB_CB(skb)->seq.input.low;
 
123
                goto resume;
 
124
        }
 
125
 
 
126
        /* Allocate new secpath or COW existing one. */
 
127
        if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
 
128
                struct sec_path *sp;
 
129
 
 
130
                sp = secpath_dup(skb->sp);
 
131
                if (!sp) {
 
132
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR);
 
133
                        goto drop;
 
134
                }
 
135
                if (skb->sp)
 
136
                        secpath_put(skb->sp);
 
137
                skb->sp = sp;
 
138
        }
 
139
 
 
140
        daddr = (xfrm_address_t *)(skb_network_header(skb) +
 
141
                                   XFRM_SPI_SKB_CB(skb)->daddroff);
 
142
        family = XFRM_SPI_SKB_CB(skb)->family;
 
143
 
 
144
        seq = 0;
 
145
        if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
 
146
                XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
 
147
                goto drop;
 
148
        }
 
149
 
 
150
        do {
 
151
                if (skb->sp->len == XFRM_MAX_DEPTH) {
 
152
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
 
153
                        goto drop;
 
154
                }
 
155
 
 
156
                x = xfrm_state_lookup(net, skb->mark, daddr, spi, nexthdr, family);
 
157
                if (x == NULL) {
 
158
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
 
159
                        xfrm_audit_state_notfound(skb, family, spi, seq);
 
160
                        goto drop;
 
161
                }
 
162
 
 
163
                skb->sp->xvec[skb->sp->len++] = x;
 
164
 
 
165
                spin_lock(&x->lock);
 
166
                if (unlikely(x->km.state != XFRM_STATE_VALID)) {
 
167
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEINVALID);
 
168
                        goto drop_unlock;
 
169
                }
 
170
 
 
171
                if ((x->encap ? x->encap->encap_type : 0) != encap_type) {
 
172
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH);
 
173
                        goto drop_unlock;
 
174
                }
 
175
 
 
176
                if (x->repl->check(x, skb, seq)) {
 
177
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
 
178
                        goto drop_unlock;
 
179
                }
 
180
 
 
181
                if (xfrm_state_check_expire(x)) {
 
182
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEEXPIRED);
 
183
                        goto drop_unlock;
 
184
                }
 
185
 
 
186
                spin_unlock(&x->lock);
 
187
 
 
188
                seq_hi = htonl(xfrm_replay_seqhi(x, seq));
 
189
 
 
190
                XFRM_SKB_CB(skb)->seq.input.low = seq;
 
191
                XFRM_SKB_CB(skb)->seq.input.hi = seq_hi;
 
192
 
 
193
                skb_dst_force(skb);
 
194
 
 
195
                nexthdr = x->type->input(x, skb);
 
196
 
 
197
                if (nexthdr == -EINPROGRESS)
 
198
                        return 0;
 
199
 
 
200
resume:
 
201
                spin_lock(&x->lock);
 
202
                if (nexthdr <= 0) {
 
203
                        if (nexthdr == -EBADMSG) {
 
204
                                xfrm_audit_state_icvfail(x, skb,
 
205
                                                         x->type->proto);
 
206
                                x->stats.integrity_failed++;
 
207
                        }
 
208
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR);
 
209
                        goto drop_unlock;
 
210
                }
 
211
 
 
212
                /* only the first xfrm gets the encap type */
 
213
                encap_type = 0;
 
214
 
 
215
                if (async && x->repl->check(x, skb, seq)) {
 
216
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
 
217
                        goto drop_unlock;
 
218
                }
 
219
 
 
220
                x->repl->advance(x, seq);
 
221
 
 
222
                x->curlft.bytes += skb->len;
 
223
                x->curlft.packets++;
 
224
 
 
225
                spin_unlock(&x->lock);
 
226
 
 
227
                XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;
 
228
 
 
229
                inner_mode = x->inner_mode;
 
230
 
 
231
                if (x->sel.family == AF_UNSPEC) {
 
232
                        inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
 
233
                        if (inner_mode == NULL)
 
234
                                goto drop;
 
235
                }
 
236
 
 
237
                if (inner_mode->input(x, skb)) {
 
238
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
 
239
                        goto drop;
 
240
                }
 
241
 
 
242
                if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
 
243
                        decaps = 1;
 
244
                        break;
 
245
                }
 
246
 
 
247
                /*
 
248
                 * We need the inner address.  However, we only get here for
 
249
                 * transport mode so the outer address is identical.
 
250
                 */
 
251
                daddr = &x->id.daddr;
 
252
                family = x->outer_mode->afinfo->family;
 
253
 
 
254
                err = xfrm_parse_spi(skb, nexthdr, &spi, &seq);
 
255
                if (err < 0) {
 
256
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
 
257
                        goto drop;
 
258
                }
 
259
        } while (!err);
 
260
 
 
261
        nf_reset(skb);
 
262
 
 
263
        if (decaps) {
 
264
                skb_dst_drop(skb);
 
265
                netif_rx(skb);
 
266
                return 0;
 
267
        } else {
 
268
                return x->inner_mode->afinfo->transport_finish(skb, async);
 
269
        }
 
270
 
 
271
drop_unlock:
 
272
        spin_unlock(&x->lock);
 
273
drop:
 
274
        kfree_skb(skb);
 
275
        return 0;
 
276
}
 
277
EXPORT_SYMBOL(xfrm_input);
 
278
 
 
279
int xfrm_input_resume(struct sk_buff *skb, int nexthdr)
 
280
{
 
281
        return xfrm_input(skb, nexthdr, 0, -1);
 
282
}
 
283
EXPORT_SYMBOL(xfrm_input_resume);
 
284
 
 
285
void __init xfrm_input_init(void)
 
286
{
 
287
        secpath_cachep = kmem_cache_create("secpath_cache",
 
288
                                           sizeof(struct sec_path),
 
289
                                           0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
 
290
                                           NULL);
 
291
}