| 
									
										
										
										
											2007-10-08 17:16:30 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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>
 | 
					
						
							|  |  |  | #include <linux/skbuff.h>
 | 
					
						
							|  |  |  | #include <linux/spinlock.h>
 | 
					
						
							|  |  |  | #include <net/dst.h>
 | 
					
						
							|  |  |  | #include <net/xfrm.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-08 17:25:08 -07:00
										 |  |  | static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev) | 
					
						
							|  |  |  | 		- skb_headroom(skb); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (nhead > 0) | 
					
						
							|  |  |  | 		return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Check tail too... */ | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err = xfrm_state_check_expire(x); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		goto err; | 
					
						
							|  |  |  | 	err = xfrm_state_check_space(x, skb); | 
					
						
							|  |  |  | err: | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-08 17:16:30 -07:00
										 |  |  | int xfrm_output(struct sk_buff *skb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct dst_entry *dst = skb->dst; | 
					
						
							|  |  |  | 	struct xfrm_state *x = dst->xfrm; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (skb->ip_summed == CHECKSUM_PARTIAL) { | 
					
						
							|  |  |  | 		err = skb_checksum_help(skb); | 
					
						
							|  |  |  | 		if (err) | 
					
						
							|  |  |  | 			goto error_nolock; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	do { | 
					
						
							|  |  |  | 		spin_lock_bh(&x->lock); | 
					
						
							|  |  |  | 		err = xfrm_state_check(x, skb); | 
					
						
							|  |  |  | 		if (err) | 
					
						
							|  |  |  | 			goto error; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-08 17:25:53 -07:00
										 |  |  | 		if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { | 
					
						
							|  |  |  | 			XFRM_SKB_CB(skb)->seq = ++x->replay.oseq; | 
					
						
							| 
									
										
										
										
											2007-10-08 17:26:34 -07:00
										 |  |  | 			if (xfrm_aevent_is_on()) | 
					
						
							|  |  |  | 				xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); | 
					
						
							| 
									
										
										
										
											2007-10-08 17:25:53 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-17 21:35:51 -07:00
										 |  |  | 		err = x->outer_mode->output(x, skb); | 
					
						
							| 
									
										
										
										
											2007-10-08 17:16:30 -07:00
										 |  |  | 		if (err) | 
					
						
							|  |  |  | 			goto error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		x->curlft.bytes += skb->len; | 
					
						
							|  |  |  | 		x->curlft.packets++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		spin_unlock_bh(&x->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-09 13:33:35 -07:00
										 |  |  | 		err = x->type->output(x, skb); | 
					
						
							|  |  |  | 		if (err) | 
					
						
							|  |  |  | 			goto error_nolock; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-08 17:16:30 -07:00
										 |  |  | 		if (!(skb->dst = dst_pop(dst))) { | 
					
						
							|  |  |  | 			err = -EHOSTUNREACH; | 
					
						
							|  |  |  | 			goto error_nolock; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		dst = skb->dst; | 
					
						
							|  |  |  | 		x = dst->xfrm; | 
					
						
							| 
									
										
										
										
											2007-10-17 21:35:51 -07:00
										 |  |  | 	} while (x && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL)); | 
					
						
							| 
									
										
										
										
											2007-10-08 17:16:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error_nolock: | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  | 	spin_unlock_bh(&x->lock); | 
					
						
							|  |  |  | 	goto error_nolock; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(xfrm_output); |