aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm/xfrm_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/xfrm/xfrm_output.c')
-rw-r--r--net/xfrm/xfrm_output.c212
1 files changed, 207 insertions, 5 deletions
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index 05926dcf5d17..9bdf16f13606 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -17,8 +17,11 @@
17#include <linux/slab.h> 17#include <linux/slab.h>
18#include <linux/spinlock.h> 18#include <linux/spinlock.h>
19#include <net/dst.h> 19#include <net/dst.h>
20#include <net/inet_ecn.h>
20#include <net/xfrm.h> 21#include <net/xfrm.h>
21 22
23#include "xfrm_inout.h"
24
22static int xfrm_output2(struct net *net, struct sock *sk, struct sk_buff *skb); 25static int xfrm_output2(struct net *net, struct sock *sk, struct sk_buff *skb);
23static int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb); 26static int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
24 27
@@ -141,6 +144,190 @@ static int xfrm6_ro_output(struct xfrm_state *x, struct sk_buff *skb)
141#endif 144#endif
142} 145}
143 146
147/* Add encapsulation header.
148 *
149 * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt.
150 */
151static int xfrm4_beet_encap_add(struct xfrm_state *x, struct sk_buff *skb)
152{
153 struct ip_beet_phdr *ph;
154 struct iphdr *top_iph;
155 int hdrlen, optlen;
156
157 hdrlen = 0;
158 optlen = XFRM_MODE_SKB_CB(skb)->optlen;
159 if (unlikely(optlen))
160 hdrlen += IPV4_BEET_PHMAXLEN - (optlen & 4);
161
162 skb_set_network_header(skb, -x->props.header_len - hdrlen +
163 (XFRM_MODE_SKB_CB(skb)->ihl - sizeof(*top_iph)));
164 if (x->sel.family != AF_INET6)
165 skb->network_header += IPV4_BEET_PHMAXLEN;
166 skb->mac_header = skb->network_header +
167 offsetof(struct iphdr, protocol);
168 skb->transport_header = skb->network_header + sizeof(*top_iph);
169
170 xfrm4_beet_make_header(skb);
171
172 ph = __skb_pull(skb, XFRM_MODE_SKB_CB(skb)->ihl - hdrlen);
173
174 top_iph = ip_hdr(skb);
175
176 if (unlikely(optlen)) {
177 if (WARN_ON(optlen < 0))
178 return -EINVAL;
179
180 ph->padlen = 4 - (optlen & 4);
181 ph->hdrlen = optlen / 8;
182 ph->nexthdr = top_iph->protocol;
183 if (ph->padlen)
184 memset(ph + 1, IPOPT_NOP, ph->padlen);
185
186 top_iph->protocol = IPPROTO_BEETPH;
187 top_iph->ihl = sizeof(struct iphdr) / 4;
188 }
189
190 top_iph->saddr = x->props.saddr.a4;
191 top_iph->daddr = x->id.daddr.a4;
192
193 return 0;
194}
195
196/* Add encapsulation header.
197 *
198 * The top IP header will be constructed per RFC 2401.
199 */
200static int xfrm4_tunnel_encap_add(struct xfrm_state *x, struct sk_buff *skb)
201{
202 struct dst_entry *dst = skb_dst(skb);
203 struct iphdr *top_iph;
204 int flags;
205
206 skb_set_inner_network_header(skb, skb_network_offset(skb));
207 skb_set_inner_transport_header(skb, skb_transport_offset(skb));
208
209 skb_set_network_header(skb, -x->props.header_len);
210 skb->mac_header = skb->network_header +
211 offsetof(struct iphdr, protocol);
212 skb->transport_header = skb->network_header + sizeof(*top_iph);
213 top_iph = ip_hdr(skb);
214
215 top_iph->ihl = 5;
216 top_iph->version = 4;
217
218 top_iph->protocol = xfrm_af2proto(skb_dst(skb)->ops->family);
219
220 /* DS disclosing depends on XFRM_SA_XFLAG_DONT_ENCAP_DSCP */
221 if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
222 top_iph->tos = 0;
223 else
224 top_iph->tos = XFRM_MODE_SKB_CB(skb)->tos;
225 top_iph->tos = INET_ECN_encapsulate(top_iph->tos,
226 XFRM_MODE_SKB_CB(skb)->tos);
227
228 flags = x->props.flags;
229 if (flags & XFRM_STATE_NOECN)
230 IP_ECN_clear(top_iph);
231
232 top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
233 0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF));
234
235 top_iph->ttl = ip4_dst_hoplimit(xfrm_dst_child(dst));
236
237 top_iph->saddr = x->props.saddr.a4;
238 top_iph->daddr = x->id.daddr.a4;
239 ip_select_ident(dev_net(dst->dev), skb, NULL);
240
241 return 0;
242}
243
244#if IS_ENABLED(CONFIG_IPV6)
245static int xfrm6_tunnel_encap_add(struct xfrm_state *x, struct sk_buff *skb)
246{
247 struct dst_entry *dst = skb_dst(skb);
248 struct ipv6hdr *top_iph;
249 int dsfield;
250
251 skb_set_inner_network_header(skb, skb_network_offset(skb));
252 skb_set_inner_transport_header(skb, skb_transport_offset(skb));
253
254 skb_set_network_header(skb, -x->props.header_len);
255 skb->mac_header = skb->network_header +
256 offsetof(struct ipv6hdr, nexthdr);
257 skb->transport_header = skb->network_header + sizeof(*top_iph);
258 top_iph = ipv6_hdr(skb);
259
260 top_iph->version = 6;
261
262 memcpy(top_iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl,
263 sizeof(top_iph->flow_lbl));
264 top_iph->nexthdr = xfrm_af2proto(skb_dst(skb)->ops->family);
265
266 if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
267 dsfield = 0;
268 else
269 dsfield = XFRM_MODE_SKB_CB(skb)->tos;
270 dsfield = INET_ECN_encapsulate(dsfield, XFRM_MODE_SKB_CB(skb)->tos);
271 if (x->props.flags & XFRM_STATE_NOECN)
272 dsfield &= ~INET_ECN_MASK;
273 ipv6_change_dsfield(top_iph, 0, dsfield);
274 top_iph->hop_limit = ip6_dst_hoplimit(xfrm_dst_child(dst));
275 top_iph->saddr = *(struct in6_addr *)&x->props.saddr;
276 top_iph->daddr = *(struct in6_addr *)&x->id.daddr;
277 return 0;
278}
279
280static int xfrm6_beet_encap_add(struct xfrm_state *x, struct sk_buff *skb)
281{
282 struct ipv6hdr *top_iph;
283 struct ip_beet_phdr *ph;
284 int optlen, hdr_len;
285
286 hdr_len = 0;
287 optlen = XFRM_MODE_SKB_CB(skb)->optlen;
288 if (unlikely(optlen))
289 hdr_len += IPV4_BEET_PHMAXLEN - (optlen & 4);
290
291 skb_set_network_header(skb, -x->props.header_len - hdr_len);
292 if (x->sel.family != AF_INET6)
293 skb->network_header += IPV4_BEET_PHMAXLEN;
294 skb->mac_header = skb->network_header +
295 offsetof(struct ipv6hdr, nexthdr);
296 skb->transport_header = skb->network_header + sizeof(*top_iph);
297 ph = __skb_pull(skb, XFRM_MODE_SKB_CB(skb)->ihl - hdr_len);
298
299 xfrm6_beet_make_header(skb);
300
301 top_iph = ipv6_hdr(skb);
302 if (unlikely(optlen)) {
303 if (WARN_ON(optlen < 0))
304 return -EINVAL;
305
306 ph->padlen = 4 - (optlen & 4);
307 ph->hdrlen = optlen / 8;
308 ph->nexthdr = top_iph->nexthdr;
309 if (ph->padlen)
310 memset(ph + 1, IPOPT_NOP, ph->padlen);
311
312 top_iph->nexthdr = IPPROTO_BEETPH;
313 }
314
315 top_iph->saddr = *(struct in6_addr *)&x->props.saddr;
316 top_iph->daddr = *(struct in6_addr *)&x->id.daddr;
317 return 0;
318}
319#endif
320
321/* Add encapsulation header.
322 *
323 * On exit, the transport header will be set to the start of the
324 * encapsulation header to be filled in by x->type->output and the mac
325 * header will be set to the nextheader (protocol for IPv4) field of the
326 * extension header directly preceding the encapsulation header, or in
327 * its absence, that of the top IP header.
328 * The value of the network header will always point to the top IP header
329 * while skb->data will point to the payload.
330 */
144static int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb) 331static int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
145{ 332{
146 int err; 333 int err;
@@ -152,7 +339,15 @@ static int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
152 IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE; 339 IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
153 skb->protocol = htons(ETH_P_IP); 340 skb->protocol = htons(ETH_P_IP);
154 341
155 return x->outer_mode->output2(x, skb); 342 switch (x->outer_mode->encap) {
343 case XFRM_MODE_BEET:
344 return xfrm4_beet_encap_add(x, skb);
345 case XFRM_MODE_TUNNEL:
346 return xfrm4_tunnel_encap_add(x, skb);
347 }
348
349 WARN_ON_ONCE(1);
350 return -EOPNOTSUPP;
156} 351}
157 352
158static int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb) 353static int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
@@ -167,11 +362,18 @@ static int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
167 skb->ignore_df = 1; 362 skb->ignore_df = 1;
168 skb->protocol = htons(ETH_P_IPV6); 363 skb->protocol = htons(ETH_P_IPV6);
169 364
170 return x->outer_mode->output2(x, skb); 365 switch (x->outer_mode->encap) {
171#else 366 case XFRM_MODE_BEET:
172 WARN_ON_ONCE(1); 367 return xfrm6_beet_encap_add(x, skb);
173 return -EOPNOTSUPP; 368 case XFRM_MODE_TUNNEL:
369 return xfrm6_tunnel_encap_add(x, skb);
370 default:
371 WARN_ON_ONCE(1);
372 return -EOPNOTSUPP;
373 }
174#endif 374#endif
375 WARN_ON_ONCE(1);
376 return -EAFNOSUPPORT;
175} 377}
176 378
177static int xfrm_outer_mode_output(struct xfrm_state *x, struct sk_buff *skb) 379static int xfrm_outer_mode_output(struct xfrm_state *x, struct sk_buff *skb)