Blame view
net/xfrm/xfrm_input.c
12.5 KB
b24413180
|
1 |
// SPDX-License-Identifier: GPL-2.0 |
1da177e4c
|
2 3 4 5 6 7 |
/* * xfrm_input.c * * Changes: * YOSHIFUJI Hideaki @USAGI * Split up af-specific portion |
a716c1197
|
8 |
* |
1da177e4c
|
9 |
*/ |
acf568ee8
|
10 11 |
#include <linux/bottom_half.h> #include <linux/interrupt.h> |
1da177e4c
|
12 13 |
#include <linux/slab.h> #include <linux/module.h> |
716062fd4
|
14 |
#include <linux/netdevice.h> |
acf568ee8
|
15 |
#include <linux/percpu.h> |
716062fd4
|
16 |
#include <net/dst.h> |
1da177e4c
|
17 18 |
#include <net/ip.h> #include <net/xfrm.h> |
049f8e2e2
|
19 20 |
#include <net/ip_tunnels.h> #include <net/ip6_tunnel.h> |
1da177e4c
|
21 |
|
acf568ee8
|
22 23 24 25 26 27 28 29 30 31 |
struct xfrm_trans_tasklet { struct tasklet_struct tasklet; struct sk_buff_head queue; }; struct xfrm_trans_cb { int (*finish)(struct net *net, struct sock *sk, struct sk_buff *skb); }; #define XFRM_TRANS_SKB_CB(__skb) ((struct xfrm_trans_cb *)&((__skb)->cb[0])) |
e18b890bb
|
32 |
static struct kmem_cache *secpath_cachep __read_mostly; |
1da177e4c
|
33 |
|
2f32b51b6
|
34 |
static DEFINE_SPINLOCK(xfrm_input_afinfo_lock); |
960fdfdeb
|
35 |
static struct xfrm_input_afinfo const __rcu *xfrm_input_afinfo[AF_INET6 + 1]; |
2f32b51b6
|
36 |
|
1995876a0
|
37 38 |
static struct gro_cells gro_cells; static struct net_device xfrm_napi_dev; |
acf568ee8
|
39 |
static DEFINE_PER_CPU(struct xfrm_trans_tasklet, xfrm_trans_tasklet); |
960fdfdeb
|
40 |
int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo) |
2f32b51b6
|
41 42 |
{ int err = 0; |
960fdfdeb
|
43 |
if (WARN_ON(afinfo->family >= ARRAY_SIZE(xfrm_input_afinfo))) |
2f32b51b6
|
44 |
return -EAFNOSUPPORT; |
960fdfdeb
|
45 |
|
2f32b51b6
|
46 47 |
spin_lock_bh(&xfrm_input_afinfo_lock); if (unlikely(xfrm_input_afinfo[afinfo->family] != NULL)) |
f31e8d4f7
|
48 |
err = -EEXIST; |
2f32b51b6
|
49 50 51 52 53 54 |
else rcu_assign_pointer(xfrm_input_afinfo[afinfo->family], afinfo); spin_unlock_bh(&xfrm_input_afinfo_lock); return err; } EXPORT_SYMBOL(xfrm_input_register_afinfo); |
960fdfdeb
|
55 |
int xfrm_input_unregister_afinfo(const struct xfrm_input_afinfo *afinfo) |
2f32b51b6
|
56 57 |
{ int err = 0; |
2f32b51b6
|
58 59 60 61 62 63 64 65 66 67 68 69 |
spin_lock_bh(&xfrm_input_afinfo_lock); if (likely(xfrm_input_afinfo[afinfo->family] != NULL)) { if (unlikely(xfrm_input_afinfo[afinfo->family] != afinfo)) err = -EINVAL; else RCU_INIT_POINTER(xfrm_input_afinfo[afinfo->family], NULL); } spin_unlock_bh(&xfrm_input_afinfo_lock); synchronize_rcu(); return err; } EXPORT_SYMBOL(xfrm_input_unregister_afinfo); |
960fdfdeb
|
70 |
static const struct xfrm_input_afinfo *xfrm_input_get_afinfo(unsigned int family) |
2f32b51b6
|
71 |
{ |
960fdfdeb
|
72 |
const struct xfrm_input_afinfo *afinfo; |
2f32b51b6
|
73 |
|
960fdfdeb
|
74 |
if (WARN_ON_ONCE(family >= ARRAY_SIZE(xfrm_input_afinfo))) |
2f32b51b6
|
75 |
return NULL; |
960fdfdeb
|
76 |
|
2f32b51b6
|
77 78 79 80 81 82 |
rcu_read_lock(); afinfo = rcu_dereference(xfrm_input_afinfo[family]); if (unlikely(!afinfo)) rcu_read_unlock(); return afinfo; } |
2f32b51b6
|
83 84 85 86 |
static int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, u8 protocol, int err) { int ret; |
960fdfdeb
|
87 |
const struct xfrm_input_afinfo *afinfo = xfrm_input_get_afinfo(family); |
2f32b51b6
|
88 89 90 91 92 |
if (!afinfo) return -EAFNOSUPPORT; ret = afinfo->callback(skb, protocol, err); |
960fdfdeb
|
93 |
rcu_read_unlock(); |
2f32b51b6
|
94 95 96 |
return ret; } |
1da177e4c
|
97 98 99 100 |
void __secpath_destroy(struct sec_path *sp) { int i; for (i = 0; i < sp->len; i++) |
dbe5b4aaa
|
101 |
xfrm_state_put(sp->xvec[i]); |
1da177e4c
|
102 103 104 105 106 107 108 |
kmem_cache_free(secpath_cachep, sp); } EXPORT_SYMBOL(__secpath_destroy); struct sec_path *secpath_dup(struct sec_path *src) { struct sec_path *sp; |
54e6ecb23
|
109 |
sp = kmem_cache_alloc(secpath_cachep, GFP_ATOMIC); |
1da177e4c
|
110 111 112 113 |
if (!sp) return NULL; sp->len = 0; |
54ef207ac
|
114 |
sp->olen = 0; |
d77e38e61
|
115 |
memset(sp->ovec, 0, sizeof(sp->ovec[XFRM_MAX_OFFLOAD_DEPTH])); |
1da177e4c
|
116 117 118 119 120 |
if (src) { int i; memcpy(sp, src, sizeof(*sp)); for (i = 0; i < sp->len; i++) |
dbe5b4aaa
|
121 |
xfrm_state_hold(sp->xvec[i]); |
1da177e4c
|
122 |
} |
55eabed60
|
123 |
refcount_set(&sp->refcnt, 1); |
1da177e4c
|
124 125 126 |
return sp; } EXPORT_SYMBOL(secpath_dup); |
b0fcee825
|
127 128 129 130 131 |
int secpath_set(struct sk_buff *skb) { struct sec_path *sp; /* Allocate new secpath or COW existing one. */ |
55eabed60
|
132 |
if (!skb->sp || refcount_read(&skb->sp->refcnt) != 1) { |
b0fcee825
|
133 134 135 136 137 138 139 140 141 142 143 |
sp = secpath_dup(skb->sp); if (!sp) return -ENOMEM; if (skb->sp) secpath_put(skb->sp); skb->sp = sp; } return 0; } EXPORT_SYMBOL(secpath_set); |
1da177e4c
|
144 |
/* Fetch spi and seq from ipsec header */ |
6067b2bab
|
145 |
int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq) |
1da177e4c
|
146 147 |
{ int offset, offset_seq; |
440725000
|
148 |
int hlen; |
1da177e4c
|
149 150 151 |
switch (nexthdr) { case IPPROTO_AH: |
440725000
|
152 |
hlen = sizeof(struct ip_auth_hdr); |
1da177e4c
|
153 154 155 156 |
offset = offsetof(struct ip_auth_hdr, spi); offset_seq = offsetof(struct ip_auth_hdr, seq_no); break; case IPPROTO_ESP: |
440725000
|
157 |
hlen = sizeof(struct ip_esp_hdr); |
1da177e4c
|
158 159 160 161 162 163 |
offset = offsetof(struct ip_esp_hdr, spi); offset_seq = offsetof(struct ip_esp_hdr, seq_no); break; case IPPROTO_COMP: if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr))) return -EINVAL; |
3e94c2dcf
|
164 |
*spi = htonl(ntohs(*(__be16 *)(skb_transport_header(skb) + 2))); |
1da177e4c
|
165 166 167 168 169 |
*seq = 0; return 0; default: return 1; } |
440725000
|
170 |
if (!pskb_may_pull(skb, hlen)) |
1da177e4c
|
171 |
return -EINVAL; |
3e94c2dcf
|
172 173 |
*spi = *(__be32 *)(skb_transport_header(skb) + offset); *seq = *(__be32 *)(skb_transport_header(skb) + offset_seq); |
1da177e4c
|
174 175 |
return 0; } |
1e2953703
|
176 |
EXPORT_SYMBOL(xfrm_parse_spi); |
1da177e4c
|
177 |
|
227620e29
|
178 179 |
int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb) { |
df9dcb458
|
180 |
struct xfrm_mode *inner_mode = x->inner_mode; |
227620e29
|
181 182 183 184 185 |
int err; err = x->outer_mode->afinfo->extract_input(x, skb); if (err) return err; |
df9dcb458
|
186 187 188 189 190 191 192 193 |
if (x->sel.family == AF_UNSPEC) { inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); if (inner_mode == NULL) return -EAFNOSUPPORT; } skb->protocol = inner_mode->afinfo->eth_proto; return inner_mode->input2(x, skb); |
227620e29
|
194 195 |
} EXPORT_SYMBOL(xfrm_prepare_input); |
716062fd4
|
196 197 |
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) { |
bd235e3cf
|
198 |
struct net *net = dev_net(skb->dev); |
716062fd4
|
199 200 |
int err; __be32 seq; |
2cd084678
|
201 |
__be32 seq_hi; |
3328715e6
|
202 |
struct xfrm_state *x = NULL; |
1bf06cd2e
|
203 |
xfrm_address_t *daddr; |
df9dcb458
|
204 |
struct xfrm_mode *inner_mode; |
049f8e2e2
|
205 |
u32 mark = skb->mark; |
4ce3dbe39
|
206 |
unsigned int family = AF_UNSPEC; |
716062fd4
|
207 |
int decaps = 0; |
1bf06cd2e
|
208 |
int async = 0; |
7785bba29
|
209 |
bool xfrm_gro = false; |
d77e38e61
|
210 211 |
bool crypto_done = false; struct xfrm_offload *xo = xfrm_offload(skb); |
1bf06cd2e
|
212 |
|
1bf06cd2e
|
213 |
if (encap_type < 0) { |
005011211
|
214 |
x = xfrm_input_state(skb); |
4ce3dbe39
|
215 216 217 218 219 220 221 222 223 |
if (unlikely(x->km.state != XFRM_STATE_VALID)) { if (x->km.state == XFRM_STATE_ACQ) XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR); else XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEINVALID); goto drop; } |
3328715e6
|
224 |
family = x->outer_mode->afinfo->family; |
7785bba29
|
225 226 227 228 229 230 231 |
/* An encap_type of -1 indicates async resumption. */ if (encap_type == -1) { async = 1; seq = XFRM_SKB_CB(skb)->seq.input.low; goto resume; } |
bcd1f8a45
|
232 |
|
7785bba29
|
233 234 235 |
/* encap_type < -1 indicates a GRO call. */ encap_type = 0; seq = XFRM_SPI_SKB_CB(skb)->seq; |
716062fd4
|
236 |
|
bcd1f8a45
|
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
if (xo && (xo->flags & CRYPTO_DONE)) { crypto_done = true; x = xfrm_input_state(skb); family = XFRM_SPI_SKB_CB(skb)->family; if (!(xo->status & CRYPTO_SUCCESS)) { if (xo->status & (CRYPTO_TRANSPORT_AH_AUTH_FAILED | CRYPTO_TRANSPORT_ESP_AUTH_FAILED | CRYPTO_TUNNEL_AH_AUTH_FAILED | CRYPTO_TUNNEL_ESP_AUTH_FAILED)) { xfrm_audit_state_icvfail(x, skb, x->type->proto); x->stats.integrity_failed++; XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR); goto drop; } |
47ebcc0bb
|
255 256 257 258 |
if (xo->status & CRYPTO_INVALID_PROTOCOL) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR); goto drop; } |
bcd1f8a45
|
259 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR); |
d77e38e61
|
260 261 |
goto drop; } |
bcd1f8a45
|
262 263 264 265 |
if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); goto drop; } |
d77e38e61
|
266 |
} |
7785bba29
|
267 |
goto lock; |
1bf06cd2e
|
268 |
} |
716062fd4
|
269 |
|
3328715e6
|
270 |
family = XFRM_SPI_SKB_CB(skb)->family; |
049f8e2e2
|
271 |
/* if tunnel is present override skb->mark value with tunnel i_key */ |
1625f4529
|
272 273 274 |
switch (family) { case AF_INET: if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4) |
049f8e2e2
|
275 |
mark = be32_to_cpu(XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4->parms.i_key); |
1625f4529
|
276 277 278 |
break; case AF_INET6: if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6) |
049f8e2e2
|
279 |
mark = be32_to_cpu(XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6->parms.i_key); |
1625f4529
|
280 |
break; |
049f8e2e2
|
281 |
} |
b0fcee825
|
282 283 284 285 |
err = secpath_set(skb); if (err) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR); goto drop; |
b2aa5e9d4
|
286 |
} |
716062fd4
|
287 |
seq = 0; |
0aa647746
|
288 |
if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) { |
59c9940ed
|
289 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); |
716062fd4
|
290 |
goto drop; |
0aa647746
|
291 |
} |
716062fd4
|
292 |
|
cb79a180f
|
293 294 |
daddr = (xfrm_address_t *)(skb_network_header(skb) + XFRM_SPI_SKB_CB(skb)->daddroff); |
716062fd4
|
295 |
do { |
0aa647746
|
296 |
if (skb->sp->len == XFRM_MAX_DEPTH) { |
59c9940ed
|
297 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR); |
716062fd4
|
298 |
goto drop; |
0aa647746
|
299 |
} |
716062fd4
|
300 |
|
049f8e2e2
|
301 |
x = xfrm_state_lookup(net, mark, daddr, spi, nexthdr, family); |
0aa647746
|
302 |
if (x == NULL) { |
59c9940ed
|
303 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES); |
afeb14b49
|
304 |
xfrm_audit_state_notfound(skb, family, spi, seq); |
716062fd4
|
305 |
goto drop; |
0aa647746
|
306 |
} |
716062fd4
|
307 |
|
b2aa5e9d4
|
308 |
skb->sp->xvec[skb->sp->len++] = x; |
7785bba29
|
309 |
lock: |
716062fd4
|
310 |
spin_lock(&x->lock); |
4c4d41f20
|
311 |
|
0aa647746
|
312 |
if (unlikely(x->km.state != XFRM_STATE_VALID)) { |
dc0565ce6
|
313 314 315 316 317 |
if (x->km.state == XFRM_STATE_ACQ) XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR); else XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEINVALID); |
716062fd4
|
318 |
goto drop_unlock; |
0aa647746
|
319 |
} |
716062fd4
|
320 |
|
3de77cf23
|
321 322 323 324 |
if ((x->encap ? x->encap->encap_type : 0) != encap_type) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH); goto drop_unlock; } |
36ae0148d
|
325 |
if (x->repl->check(x, skb, seq)) { |
59c9940ed
|
326 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR); |
716062fd4
|
327 |
goto drop_unlock; |
0aa647746
|
328 |
} |
716062fd4
|
329 |
|
0aa647746
|
330 |
if (xfrm_state_check_expire(x)) { |
59c9940ed
|
331 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEEXPIRED); |
716062fd4
|
332 |
goto drop_unlock; |
0aa647746
|
333 |
} |
716062fd4
|
334 |
|
0ebea8ef3
|
335 |
spin_unlock(&x->lock); |
68c11e98e
|
336 337 338 339 |
if (xfrm_tunnel_check(skb, x, family)) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); goto drop; } |
2cd084678
|
340 |
seq_hi = htonl(xfrm_replay_seqhi(x, seq)); |
1ce3644ad
|
341 |
XFRM_SKB_CB(skb)->seq.input.low = seq; |
2cd084678
|
342 |
XFRM_SKB_CB(skb)->seq.input.hi = seq_hi; |
1bf06cd2e
|
343 |
|
3bc07321c
|
344 |
skb_dst_force(skb); |
071d36bf2
|
345 |
dev_hold(skb->dev); |
3bc07321c
|
346 |
|
d77e38e61
|
347 348 349 350 |
if (crypto_done) nexthdr = x->type_offload->input_tail(x, skb); else nexthdr = x->type->input(x, skb); |
0ebea8ef3
|
351 |
|
1bf06cd2e
|
352 353 |
if (nexthdr == -EINPROGRESS) return 0; |
1bf06cd2e
|
354 |
resume: |
071d36bf2
|
355 |
dev_put(skb->dev); |
0ebea8ef3
|
356 |
spin_lock(&x->lock); |
668dc8af3
|
357 |
if (nexthdr <= 0) { |
9dd3245a2
|
358 359 360 |
if (nexthdr == -EBADMSG) { xfrm_audit_state_icvfail(x, skb, x->type->proto); |
668dc8af3
|
361 |
x->stats.integrity_failed++; |
9dd3245a2
|
362 |
} |
59c9940ed
|
363 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR); |
716062fd4
|
364 |
goto drop_unlock; |
668dc8af3
|
365 |
} |
716062fd4
|
366 |
|
716062fd4
|
367 368 |
/* only the first xfrm gets the encap type */ encap_type = 0; |
3b59df46a
|
369 |
if (async && x->repl->recheck(x, skb, seq)) { |
bcf66bf54
|
370 371 372 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR); goto drop_unlock; } |
9fdc4883d
|
373 |
x->repl->advance(x, seq); |
716062fd4
|
374 375 376 377 378 |
x->curlft.bytes += skb->len; x->curlft.packets++; spin_unlock(&x->lock); |
60d5fcfb1
|
379 |
XFRM_MODE_SKB_CB(skb)->protocol = nexthdr; |
df9dcb458
|
380 381 382 383 |
inner_mode = x->inner_mode; if (x->sel.family == AF_UNSPEC) { inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); |
cb866e329
|
384 385 |
if (inner_mode == NULL) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); |
df9dcb458
|
386 |
goto drop; |
cb866e329
|
387 |
} |
df9dcb458
|
388 389 390 |
} if (inner_mode->input(x, skb)) { |
59c9940ed
|
391 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); |
716062fd4
|
392 |
goto drop; |
0aa647746
|
393 |
} |
716062fd4
|
394 395 396 397 398 |
if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) { decaps = 1; break; } |
1bf06cd2e
|
399 400 401 402 403 |
/* * We need the inner address. However, we only get here for * transport mode so the outer address is identical. */ daddr = &x->id.daddr; |
2fcb45b6b
|
404 |
family = x->outer_mode->afinfo->family; |
1bf06cd2e
|
405 |
|
716062fd4
|
406 |
err = xfrm_parse_spi(skb, nexthdr, &spi, &seq); |
0aa647746
|
407 |
if (err < 0) { |
59c9940ed
|
408 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); |
716062fd4
|
409 |
goto drop; |
0aa647746
|
410 |
} |
716062fd4
|
411 |
} while (!err); |
3328715e6
|
412 413 414 |
err = xfrm_rcv_cb(skb, family, x->type->proto, 0); if (err) goto drop; |
716062fd4
|
415 416 417 |
nf_reset(skb); if (decaps) { |
23e9fcfef
|
418 419 |
if (skb->sp) skb->sp->olen = 0; |
adf30907d
|
420 |
skb_dst_drop(skb); |
1995876a0
|
421 |
gro_cells_receive(&gro_cells, skb); |
716062fd4
|
422 423 |
return 0; } else { |
7785bba29
|
424 425 426 |
xo = xfrm_offload(skb); if (xo) xfrm_gro = xo->flags & XFRM_GRO; |
cfcf99f98
|
427 |
err = x->inner_mode->afinfo->transport_finish(skb, xfrm_gro || async); |
7785bba29
|
428 |
if (xfrm_gro) { |
23e9fcfef
|
429 430 |
if (skb->sp) skb->sp->olen = 0; |
7785bba29
|
431 432 433 434 435 436 |
skb_dst_drop(skb); gro_cells_receive(&gro_cells, skb); return err; } return err; |
716062fd4
|
437 438 439 440 |
} drop_unlock: spin_unlock(&x->lock); |
716062fd4
|
441 |
drop: |
3328715e6
|
442 |
xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1); |
716062fd4
|
443 444 445 446 |
kfree_skb(skb); return 0; } EXPORT_SYMBOL(xfrm_input); |
1bf06cd2e
|
447 448 449 450 451 |
int xfrm_input_resume(struct sk_buff *skb, int nexthdr) { return xfrm_input(skb, nexthdr, 0, -1); } EXPORT_SYMBOL(xfrm_input_resume); |
acf568ee8
|
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 |
static void xfrm_trans_reinject(unsigned long data) { struct xfrm_trans_tasklet *trans = (void *)data; struct sk_buff_head queue; struct sk_buff *skb; __skb_queue_head_init(&queue); skb_queue_splice_init(&trans->queue, &queue); while ((skb = __skb_dequeue(&queue))) XFRM_TRANS_SKB_CB(skb)->finish(dev_net(skb->dev), NULL, skb); } int xfrm_trans_queue(struct sk_buff *skb, int (*finish)(struct net *, struct sock *, struct sk_buff *)) { struct xfrm_trans_tasklet *trans; trans = this_cpu_ptr(&xfrm_trans_tasklet); if (skb_queue_len(&trans->queue) >= netdev_max_backlog) return -ENOBUFS; XFRM_TRANS_SKB_CB(skb)->finish = finish; skb_queue_tail(&trans->queue, skb); tasklet_schedule(&trans->tasklet); return 0; } EXPORT_SYMBOL(xfrm_trans_queue); |
1da177e4c
|
482 483 |
void __init xfrm_input_init(void) { |
1995876a0
|
484 |
int err; |
acf568ee8
|
485 |
int i; |
1995876a0
|
486 487 488 489 490 |
init_dummy_netdev(&xfrm_napi_dev); err = gro_cells_init(&gro_cells, &xfrm_napi_dev); if (err) gro_cells.cells = NULL; |
1da177e4c
|
491 492 |
secpath_cachep = kmem_cache_create("secpath_cache", sizeof(struct sec_path), |
e5d679f33
|
493 |
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, |
20c2df83d
|
494 |
NULL); |
acf568ee8
|
495 496 497 498 499 500 501 502 503 |
for_each_possible_cpu(i) { struct xfrm_trans_tasklet *trans; trans = &per_cpu(xfrm_trans_tasklet, i); __skb_queue_head_init(&trans->queue); tasklet_init(&trans->tasklet, xfrm_trans_reinject, (unsigned long)trans); } |
1da177e4c
|
504 |
} |