1406ef77cSHerbert Xu /* 2406ef77cSHerbert Xu * xfrm_output.c - Common IPsec encapsulation code. 3406ef77cSHerbert Xu * 4406ef77cSHerbert Xu * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au> 5406ef77cSHerbert Xu * 6406ef77cSHerbert Xu * This program is free software; you can redistribute it and/or 7406ef77cSHerbert Xu * modify it under the terms of the GNU General Public License 8406ef77cSHerbert Xu * as published by the Free Software Foundation; either version 9406ef77cSHerbert Xu * 2 of the License, or (at your option) any later version. 10406ef77cSHerbert Xu */ 11406ef77cSHerbert Xu 12406ef77cSHerbert Xu #include <linux/errno.h> 13406ef77cSHerbert Xu #include <linux/module.h> 14406ef77cSHerbert Xu #include <linux/netdevice.h> 15406ef77cSHerbert Xu #include <linux/skbuff.h> 16406ef77cSHerbert Xu #include <linux/spinlock.h> 17406ef77cSHerbert Xu #include <linux/time.h> 18406ef77cSHerbert Xu #include <net/dst.h> 19406ef77cSHerbert Xu #include <net/xfrm.h> 20406ef77cSHerbert Xu 2183815deaSHerbert Xu static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) 2283815deaSHerbert Xu { 2383815deaSHerbert Xu int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev) 2483815deaSHerbert Xu - skb_headroom(skb); 2583815deaSHerbert Xu 2683815deaSHerbert Xu if (nhead > 0) 2783815deaSHerbert Xu return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC); 2883815deaSHerbert Xu 2983815deaSHerbert Xu /* Check tail too... */ 3083815deaSHerbert Xu return 0; 3183815deaSHerbert Xu } 3283815deaSHerbert Xu 3383815deaSHerbert Xu static int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb) 3483815deaSHerbert Xu { 3583815deaSHerbert Xu int err = xfrm_state_check_expire(x); 3683815deaSHerbert Xu if (err < 0) 3783815deaSHerbert Xu goto err; 3883815deaSHerbert Xu err = xfrm_state_check_space(x, skb); 3983815deaSHerbert Xu err: 4083815deaSHerbert Xu return err; 4183815deaSHerbert Xu } 4283815deaSHerbert Xu 43406ef77cSHerbert Xu int xfrm_output(struct sk_buff *skb) 44406ef77cSHerbert Xu { 45406ef77cSHerbert Xu struct dst_entry *dst = skb->dst; 46406ef77cSHerbert Xu struct xfrm_state *x = dst->xfrm; 47406ef77cSHerbert Xu int err; 48406ef77cSHerbert Xu 49406ef77cSHerbert Xu if (skb->ip_summed == CHECKSUM_PARTIAL) { 50406ef77cSHerbert Xu err = skb_checksum_help(skb); 51406ef77cSHerbert Xu if (err) 52406ef77cSHerbert Xu goto error_nolock; 53406ef77cSHerbert Xu } 54406ef77cSHerbert Xu 55406ef77cSHerbert Xu do { 56406ef77cSHerbert Xu spin_lock_bh(&x->lock); 57406ef77cSHerbert Xu err = xfrm_state_check(x, skb); 58406ef77cSHerbert Xu if (err) 59406ef77cSHerbert Xu goto error; 60406ef77cSHerbert Xu 61406ef77cSHerbert Xu err = x->mode->output(x, skb); 62406ef77cSHerbert Xu if (err) 63406ef77cSHerbert Xu goto error; 64406ef77cSHerbert Xu 65406ef77cSHerbert Xu err = x->type->output(x, skb); 66406ef77cSHerbert Xu if (err) 67406ef77cSHerbert Xu goto error; 68406ef77cSHerbert Xu 69406ef77cSHerbert Xu x->curlft.bytes += skb->len; 70406ef77cSHerbert Xu x->curlft.packets++; 71406ef77cSHerbert Xu 72406ef77cSHerbert Xu if (x->props.mode == XFRM_MODE_ROUTEOPTIMIZATION) 73406ef77cSHerbert Xu x->lastused = get_seconds(); 74406ef77cSHerbert Xu 75406ef77cSHerbert Xu spin_unlock_bh(&x->lock); 76406ef77cSHerbert Xu 77406ef77cSHerbert Xu skb_reset_network_header(skb); 78406ef77cSHerbert Xu 79406ef77cSHerbert Xu if (!(skb->dst = dst_pop(dst))) { 80406ef77cSHerbert Xu err = -EHOSTUNREACH; 81406ef77cSHerbert Xu goto error_nolock; 82406ef77cSHerbert Xu } 83406ef77cSHerbert Xu dst = skb->dst; 84406ef77cSHerbert Xu x = dst->xfrm; 85406ef77cSHerbert Xu } while (x && (x->props.mode != XFRM_MODE_TUNNEL)); 86406ef77cSHerbert Xu 87406ef77cSHerbert Xu err = 0; 88406ef77cSHerbert Xu 89406ef77cSHerbert Xu error_nolock: 90406ef77cSHerbert Xu return err; 91406ef77cSHerbert Xu error: 92406ef77cSHerbert Xu spin_unlock_bh(&x->lock); 93406ef77cSHerbert Xu goto error_nolock; 94406ef77cSHerbert Xu } 95406ef77cSHerbert Xu EXPORT_SYMBOL_GPL(xfrm_output); 96