Blame view
net/xfrm/xfrm_output.c
4.27 KB
406ef77c8
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* * xfrm_output.c - Common IPsec encapsulation code. * * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include <linux/errno.h> #include <linux/module.h> #include <linux/netdevice.h> |
862b82c6f
|
15 |
#include <linux/netfilter.h> |
406ef77c8
|
16 17 |
#include <linux/skbuff.h> #include <linux/spinlock.h> |
406ef77c8
|
18 19 |
#include <net/dst.h> #include <net/xfrm.h> |
c6581a457
|
20 |
static int xfrm_output2(struct sk_buff *skb); |
83815dea4
|
21 22 |
static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) { |
550ade843
|
23 24 |
struct dst_entry *dst = skb->dst; int nhead = dst->header_len + LL_RESERVED_SPACE(dst->dev) |
83815dea4
|
25 |
- skb_headroom(skb); |
f5184d267
|
26 |
int ntail = dst->dev->needed_tailroom - skb_tailroom(skb); |
83815dea4
|
27 |
|
f5184d267
|
28 29 |
if (nhead > 0 || ntail > 0) return pskb_expand_head(skb, nhead, ntail, GFP_ATOMIC); |
83815dea4
|
30 |
|
83815dea4
|
31 32 |
return 0; } |
c6581a457
|
33 |
static int xfrm_output_one(struct sk_buff *skb, int err) |
406ef77c8
|
34 35 36 |
{ struct dst_entry *dst = skb->dst; struct xfrm_state *x = dst->xfrm; |
406ef77c8
|
37 |
|
c6581a457
|
38 39 |
if (err <= 0) goto resume; |
406ef77c8
|
40 41 |
do { |
910ef70aa
|
42 |
err = xfrm_state_check_space(x, skb); |
b15c4bcd1
|
43 44 |
if (err) { XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR); |
910ef70aa
|
45 |
goto error_nolock; |
b15c4bcd1
|
46 |
} |
910ef70aa
|
47 |
|
a2deb6d26
|
48 |
err = x->outer_mode->output(x, skb); |
b15c4bcd1
|
49 50 |
if (err) { XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEMODEERROR); |
910ef70aa
|
51 |
goto error_nolock; |
b15c4bcd1
|
52 |
} |
a2deb6d26
|
53 |
|
406ef77c8
|
54 |
spin_lock_bh(&x->lock); |
910ef70aa
|
55 |
err = xfrm_state_check_expire(x); |
b15c4bcd1
|
56 57 |
if (err) { XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEEXPIRED); |
406ef77c8
|
58 |
goto error; |
b15c4bcd1
|
59 |
} |
406ef77c8
|
60 |
|
436a0a402
|
61 |
if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { |
b318e0e4e
|
62 |
XFRM_SKB_CB(skb)->seq.output = ++x->replay.oseq; |
e1af9f270
|
63 |
if (unlikely(x->replay.oseq == 0)) { |
9472c9ef6
|
64 |
XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATESEQERROR); |
e1af9f270
|
65 |
x->replay.oseq--; |
afeb14b49
|
66 |
xfrm_audit_state_replay_overflow(x, skb); |
dbb1db8b5
|
67 |
err = -EOVERFLOW; |
e1af9f270
|
68 69 |
goto error; } |
cdf7e668d
|
70 71 |
if (xfrm_aevent_is_on()) xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); |
436a0a402
|
72 |
} |
406ef77c8
|
73 74 |
x->curlft.bytes += skb->len; x->curlft.packets++; |
406ef77c8
|
75 |
spin_unlock_bh(&x->lock); |
b7c6538cd
|
76 |
err = x->type->output(x, skb); |
fcb8c156c
|
77 78 |
if (err == -EINPROGRESS) goto out_exit; |
c6581a457
|
79 80 |
resume: |
0aa647746
|
81 82 |
if (err) { XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEPROTOERROR); |
b7c6538cd
|
83 |
goto error_nolock; |
0aa647746
|
84 |
} |
b7c6538cd
|
85 |
|
406ef77c8
|
86 |
if (!(skb->dst = dst_pop(dst))) { |
0aa647746
|
87 |
XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR); |
406ef77c8
|
88 89 90 91 92 |
err = -EHOSTUNREACH; goto error_nolock; } dst = skb->dst; x = dst->xfrm; |
13996378e
|
93 |
} while (x && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL)); |
406ef77c8
|
94 95 |
err = 0; |
862b82c6f
|
96 |
out_exit: |
406ef77c8
|
97 98 99 |
return err; error: spin_unlock_bh(&x->lock); |
862b82c6f
|
100 101 102 103 |
error_nolock: kfree_skb(skb); goto out_exit; } |
c6581a457
|
104 |
int xfrm_output_resume(struct sk_buff *skb, int err) |
862b82c6f
|
105 |
{ |
c6581a457
|
106 |
while (likely((err = xfrm_output_one(skb, err)) == 0)) { |
862b82c6f
|
107 108 109 110 |
nf_reset(skb); err = skb->dst->ops->local_out(skb); if (unlikely(err != 1)) |
c6581a457
|
111 |
goto out; |
862b82c6f
|
112 |
|
c1e24df27
|
113 |
if (!skb->dst->xfrm) |
862b82c6f
|
114 |
return dst_output(skb); |
df9dcb458
|
115 |
err = nf_hook(skb->dst->ops->family, |
294b4baf2
|
116 |
NF_INET_POST_ROUTING, skb, |
862b82c6f
|
117 118 |
NULL, skb->dst->dev, xfrm_output2); if (unlikely(err != 1)) |
c6581a457
|
119 |
goto out; |
862b82c6f
|
120 |
} |
c6581a457
|
121 122 123 124 |
if (err == -EINPROGRESS) err = 0; out: |
862b82c6f
|
125 126 |
return err; } |
c6581a457
|
127 |
EXPORT_SYMBOL_GPL(xfrm_output_resume); |
862b82c6f
|
128 |
|
c6581a457
|
129 |
static int xfrm_output2(struct sk_buff *skb) |
862b82c6f
|
130 |
{ |
c6581a457
|
131 132 |
return xfrm_output_resume(skb, 1); } |
862b82c6f
|
133 |
|
c6581a457
|
134 135 136 |
static int xfrm_output_gso(struct sk_buff *skb) { struct sk_buff *segs; |
862b82c6f
|
137 138 139 |
segs = skb_gso_segment(skb, 0); kfree_skb(skb); |
801678c5a
|
140 |
if (IS_ERR(segs)) |
862b82c6f
|
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
return PTR_ERR(segs); do { struct sk_buff *nskb = segs->next; int err; segs->next = NULL; err = xfrm_output2(segs); if (unlikely(err)) { while ((segs = nskb)) { nskb = segs->next; segs->next = NULL; kfree_skb(segs); } return err; } segs = nskb; } while (segs); return 0; |
406ef77c8
|
163 |
} |
c6581a457
|
164 165 166 167 168 169 170 171 172 173 174 |
int xfrm_output(struct sk_buff *skb) { int err; if (skb_is_gso(skb)) return xfrm_output_gso(skb); if (skb->ip_summed == CHECKSUM_PARTIAL) { err = skb_checksum_help(skb); if (err) { |
0aa647746
|
175 |
XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR); |
c6581a457
|
176 177 178 179 180 181 182 |
kfree_skb(skb); return err; } } return xfrm_output2(skb); } |
df9dcb458
|
183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb) { struct xfrm_mode *inner_mode; if (x->sel.family == AF_UNSPEC) inner_mode = xfrm_ip2inner_mode(x, xfrm_af2proto(skb->dst->ops->family)); else inner_mode = x->inner_mode; if (inner_mode == NULL) return -EAFNOSUPPORT; return inner_mode->afinfo->extract_output(x, skb); } |
406ef77c8
|
197 |
EXPORT_SYMBOL_GPL(xfrm_output); |
df9dcb458
|
198 |
EXPORT_SYMBOL_GPL(xfrm_inner_extract_output); |