Blame view
net/xfrm/xfrm_output.c
4.26 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 |
#include <linux/skbuff.h> |
5a0e3ad6a
|
17 |
#include <linux/slab.h> |
406ef77c8
|
18 |
#include <linux/spinlock.h> |
406ef77c8
|
19 20 |
#include <net/dst.h> #include <net/xfrm.h> |
c6581a457
|
21 |
static int xfrm_output2(struct sk_buff *skb); |
83815dea4
|
22 23 |
static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) { |
adf30907d
|
24 |
struct dst_entry *dst = skb_dst(skb); |
550ade843
|
25 |
int nhead = dst->header_len + LL_RESERVED_SPACE(dst->dev) |
83815dea4
|
26 |
- skb_headroom(skb); |
f5184d267
|
27 |
int ntail = dst->dev->needed_tailroom - skb_tailroom(skb); |
83815dea4
|
28 |
|
d01dbeb6a
|
29 30 31 32 33 34 35 36 |
if (nhead <= 0) { if (ntail <= 0) return 0; nhead = 0; } else if (ntail < 0) ntail = 0; return pskb_expand_head(skb, nhead, ntail, GFP_ATOMIC); |
83815dea4
|
37 |
} |
c6581a457
|
38 |
static int xfrm_output_one(struct sk_buff *skb, int err) |
406ef77c8
|
39 |
{ |
adf30907d
|
40 |
struct dst_entry *dst = skb_dst(skb); |
406ef77c8
|
41 |
struct xfrm_state *x = dst->xfrm; |
a6483b790
|
42 |
struct net *net = xs_net(x); |
406ef77c8
|
43 |
|
c6581a457
|
44 45 |
if (err <= 0) goto resume; |
406ef77c8
|
46 47 |
do { |
910ef70aa
|
48 |
err = xfrm_state_check_space(x, skb); |
b15c4bcd1
|
49 |
if (err) { |
59c9940ed
|
50 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR); |
910ef70aa
|
51 |
goto error_nolock; |
b15c4bcd1
|
52 |
} |
910ef70aa
|
53 |
|
a2deb6d26
|
54 |
err = x->outer_mode->output(x, skb); |
b15c4bcd1
|
55 |
if (err) { |
59c9940ed
|
56 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR); |
910ef70aa
|
57 |
goto error_nolock; |
b15c4bcd1
|
58 |
} |
a2deb6d26
|
59 |
|
406ef77c8
|
60 |
spin_lock_bh(&x->lock); |
910ef70aa
|
61 |
err = xfrm_state_check_expire(x); |
b15c4bcd1
|
62 |
if (err) { |
59c9940ed
|
63 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEEXPIRED); |
406ef77c8
|
64 |
goto error; |
b15c4bcd1
|
65 |
} |
406ef77c8
|
66 |
|
9fdc4883d
|
67 68 69 70 |
err = x->repl->overflow(x, skb); if (err) { XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATESEQERROR); goto error; |
436a0a402
|
71 |
} |
406ef77c8
|
72 73 |
x->curlft.bytes += skb->len; x->curlft.packets++; |
406ef77c8
|
74 |
spin_unlock_bh(&x->lock); |
3bc07321c
|
75 |
skb_dst_force(skb); |
b7c6538cd
|
76 |
err = x->type->output(x, skb); |
fcb8c156c
|
77 78 |
if (err == -EINPROGRESS) goto out_exit; |
c6581a457
|
79 80 |
resume: |
0aa647746
|
81 |
if (err) { |
59c9940ed
|
82 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEPROTOERROR); |
b7c6538cd
|
83 |
goto error_nolock; |
0aa647746
|
84 |
} |
b7c6538cd
|
85 |
|
8764ab2ca
|
86 |
dst = skb_dst_pop(skb); |
adf30907d
|
87 |
if (!dst) { |
59c9940ed
|
88 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR); |
406ef77c8
|
89 90 91 |
err = -EHOSTUNREACH; goto error_nolock; } |
e433430a0
|
92 |
skb_dst_set(skb, dst); |
406ef77c8
|
93 |
x = dst->xfrm; |
13996378e
|
94 |
} while (x && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL)); |
406ef77c8
|
95 96 |
err = 0; |
862b82c6f
|
97 |
out_exit: |
406ef77c8
|
98 99 100 |
return err; error: spin_unlock_bh(&x->lock); |
862b82c6f
|
101 102 103 104 |
error_nolock: kfree_skb(skb); goto out_exit; } |
c6581a457
|
105 |
int xfrm_output_resume(struct sk_buff *skb, int err) |
862b82c6f
|
106 |
{ |
c6581a457
|
107 |
while (likely((err = xfrm_output_one(skb, err)) == 0)) { |
862b82c6f
|
108 |
nf_reset(skb); |
adf30907d
|
109 |
err = skb_dst(skb)->ops->local_out(skb); |
862b82c6f
|
110 |
if (unlikely(err != 1)) |
c6581a457
|
111 |
goto out; |
862b82c6f
|
112 |
|
adf30907d
|
113 |
if (!skb_dst(skb)->xfrm) |
862b82c6f
|
114 |
return dst_output(skb); |
adf30907d
|
115 |
err = nf_hook(skb_dst(skb)->ops->family, |
294b4baf2
|
116 |
NF_INET_POST_ROUTING, skb, |
adf30907d
|
117 |
NULL, skb_dst(skb)->dev, xfrm_output2); |
862b82c6f
|
118 |
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 |
int xfrm_output(struct sk_buff *skb) { |
adf30907d
|
167 |
struct net *net = dev_net(skb_dst(skb)->dev); |
c6581a457
|
168 169 170 171 172 173 174 175 |
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) { |
59c9940ed
|
176 |
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR); |
c6581a457
|
177 178 179 180 181 182 183 |
kfree_skb(skb); return err; } } return xfrm_output2(skb); } |
df9dcb458
|
184 185 186 187 188 189 |
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, |
adf30907d
|
190 |
xfrm_af2proto(skb_dst(skb)->ops->family)); |
df9dcb458
|
191 192 193 194 195 196 197 |
else inner_mode = x->inner_mode; if (inner_mode == NULL) return -EAFNOSUPPORT; return inner_mode->afinfo->extract_output(x, skb); } |
406ef77c8
|
198 |
EXPORT_SYMBOL_GPL(xfrm_output); |
df9dcb458
|
199 |
EXPORT_SYMBOL_GPL(xfrm_inner_extract_output); |