ipsec_rcv.c

来自「FREESWAN VPN源代码包」· C语言 代码 · 共 2,240 行 · 第 1/5 页

C
2,240
字号
/* * receive code * Copyright (C) 1996, 1997  John Ioannidis. * Copyright (C) 1998, 1999, 2000, 2001  Richard Guy Briggs. *  * 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.  See <http://www.fsf.org/copyleft/gpl.txt>. *  * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * for more details. */char ipsec_rcv_c_version[] = "RCSID $Id: ipsec_rcv.c,v 1.15 2003/07/04 19:07:34 ken Exp $";#include <linux/config.h>#include <linux/version.h>#define __NO_VERSION__#include <linux/module.h>#include <linux/kernel.h> /* printk() */#define IPSEC_KLIPS1_COMPAT 1#include "ipsec_param.h"#ifdef MALLOC_SLAB# include <linux/slab.h> /* kmalloc() */#else /* MALLOC_SLAB */# include <linux/malloc.h> /* kmalloc() */#endif /* MALLOC_SLAB */#include <linux/errno.h>  /* error codes */#include <linux/types.h>  /* size_t */#include <linux/interrupt.h> /* mark_bh */#include <linux/netdevice.h>   /* struct device, and other headers */#include <linux/etherdevice.h> /* eth_type_trans */#include <linux/ip.h>          /* struct iphdr */#include <linux/skbuff.h>#include <freeswan.h>#ifdef SPINLOCK# ifdef SPINLOCK_23#  include <linux/spinlock.h> /* *lock* */# else /* SPINLOCK_23 */#  include <asm/spinlock.h> /* *lock* */# endif /* SPINLOCK_23 */#endif /* SPINLOCK */#ifdef NET_21# include <asm/uaccess.h># include <linux/in6.h># define proto_priv cb#endif /* NET21 */#include <asm/checksum.h>#include <net/ip.h>#include "radij.h"#include "ipsec_encap.h"#include "ipsec_sa.h"#include "ipsec_radij.h"#include "ipsec_netlink.h"#include "ipsec_xform.h"#include "ipsec_tunnel.h"#include "ipsec_rcv.h"#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH)# include "ipsec_ah.h"#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */#ifdef CONFIG_IPSEC_ESP# include "ipsec_esp.h"#endif /* !CONFIG_IPSEC_ESP */#ifdef CONFIG_IPSEC_IPCOMP# include "ipcomp.h"#endif /* CONFIG_IPSEC_COMP */#include <pfkeyv2.h>#include <pfkey.h>#include "ipsec_proto.h"#include "ipsec_alg.h"#ifdef CONFIG_IPSEC_NAT_TRAVERSAL#include <linux/udp.h>#endif#ifdef CONFIG_IPSEC_DEBUGint debug_ah = 0;int debug_esp = 0;int debug_rcv = 0;#endif /* CONFIG_IPSEC_DEBUG */int sysctl_ipsec_inbound_policy_check = 1;#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH)__u32 zeroes[AH_AMAX];#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) *//* * Check-replay-window routine, adapted from the original  * by J. Hughes, from draft-ietf-ipsec-esp-des-md5-03.txt * *  This is a routine that implements a 64 packet window. This is intend- *  ed on being an implementation sample. */DEBUG_NO_STATIC intipsec_checkreplaywindow(struct ipsec_sa*tdbp, __u32 seq){	__u32 diff;		if (tdbp->tdb_replaywin == 0)	/* replay shut off */		return 1;	if (seq == 0) 		return 0;		/* first == 0 or wrapped */	/* new larger sequence number */	if (seq > tdbp->tdb_replaywin_lastseq) {		return 1;		/* larger is good */	}	diff = tdbp->tdb_replaywin_lastseq - seq;	/* too old or wrapped */ /* if wrapped, kill off SA? */	if (diff >= tdbp->tdb_replaywin) {		return 0;	}	/* this packet already seen */	if (tdbp->tdb_replaywin_bitmap & (1 << diff))		return 0;	return 1;			/* out of order but good */}DEBUG_NO_STATIC intipsec_updatereplaywindow(struct ipsec_sa*tdbp, __u32 seq){	__u32 diff;		if (tdbp->tdb_replaywin == 0)	/* replay shut off */		return 1;	if (seq == 0) 		return 0;		/* first == 0 or wrapped */	/* new larger sequence number */	if (seq > tdbp->tdb_replaywin_lastseq) {		diff = seq - tdbp->tdb_replaywin_lastseq;		/* In win, set bit for this pkt */		if (diff < tdbp->tdb_replaywin)			tdbp->tdb_replaywin_bitmap =				(tdbp->tdb_replaywin_bitmap << diff) | 1;		else			/* This packet has way larger seq num */			tdbp->tdb_replaywin_bitmap = 1;		if(seq - tdbp->tdb_replaywin_lastseq - 1 > tdbp->tdb_replaywin_maxdiff) {			tdbp->tdb_replaywin_maxdiff = seq - tdbp->tdb_replaywin_lastseq - 1;		}		tdbp->tdb_replaywin_lastseq = seq;		return 1;		/* larger is good */	}	diff = tdbp->tdb_replaywin_lastseq - seq;	/* too old or wrapped */ /* if wrapped, kill off SA? */	if (diff >= tdbp->tdb_replaywin) {/*		if(seq < 0.25*max && tdbp->tdb_replaywin_lastseq > 0.75*max) {			deltdbchain(tdbp);		}*/			return 0;	}	/* this packet already seen */	if (tdbp->tdb_replaywin_bitmap & (1 << diff))		return 0;	tdbp->tdb_replaywin_bitmap |= (1 << diff);	/* mark as seen */	return 1;			/* out of order but good */}int#ifdef PROTO_HANDLER_SINGLE_PARMipsec_rcv(struct sk_buff *skb)#else /* PROTO_HANDLER_SINGLE_PARM */#ifdef NET_21ipsec_rcv(struct sk_buff *skb, unsigned short xlen)#else /* NET_21 */ipsec_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, 		__u32 daddr_unused, unsigned short xlen, __u32 saddr,                                   int redo, struct inet_protocol *protocol)#endif /* NET_21 */#endif /* PROTO_HANDLER_SINGLE_PARM */{#ifdef NET_21#ifdef CONFIG_IPSEC_DEBUG	struct device *dev = skb->dev;#endif /* CONFIG_IPSEC_DEBUG */#endif /* NET_21 */	unsigned char protoc;	struct iphdr *ipp;	int authlen = 0;#ifdef CONFIG_IPSEC_ESP	struct esp *espp = NULL;	int esphlen = 0;#ifdef CONFIG_IPSEC_ENC_3DES	__u32 iv[ESP_IV_MAXSZ_INT];#endif /* !CONFIG_IPSEC_ENC_3DES */#endif /* !CONFIG_IPSEC_ESP */#ifdef CONFIG_IPSEC_AH	struct ah *ahp = NULL;	int ahhlen = 0;#if defined (CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1)	struct iphdr ipo;#endif#endif /* CONFIG_IPSEC_AH */	unsigned char *authenticator = NULL;	union {		MD5_CTX		md5;		SHA1_CTX	sha1;	} tctx;	__u8 hash[AH_AMAX];#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH)#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */#ifdef CONFIG_IPSEC_IPCOMP	struct ipcomphdr*compp = NULL;#endif /* CONFIG_IPSEC_IPCOMP */	int hard_header_len;	int iphlen;	unsigned char *dat;	struct ipsec_sa *tdbp = NULL;	struct sa_id said;	struct net_device_stats *stats = NULL;		/* This device's statistics */	struct device *ipsecdev = NULL, *prvdev;	struct ipsecpriv *prv;	char name[9];	char sa[SATOA_BUF];	size_t sa_len;	char ipaddr_txt[ADDRTOA_BUF];	int i;	struct in_addr ipaddr;	__u8 next_header = 0;	__u8 proto;	#ifdef CONFIG_IPSEC_ESP	int pad = 0, padlen;#endif /* CONFIG_IPSEC_ESP */	int ilen;	/* content to be decrypted/authenticated */	int len;	/* packet length */	int replay = 0;	/* replay value in AH or ESP packet */	__u8 *idat;	/* pointer to content to be decrypted/authenticated */	struct ipsec_sa* tdbprev = NULL;	/* previous SA from outside of packet */	struct ipsec_sa* tdbnext = NULL;	/* next SA towards inside of packet */#ifdef INBOUND_POLICY_CHECK_eroute	struct sockaddr_encap matcher;	/* eroute search key */	struct eroute *er;	struct ipsec_sa* policy_tdb = NULL;	struct sa_id policy_said;	struct sockaddr_encap policy_eaddr;	struct sockaddr_encap policy_emask;#endif /* INBOUND_POLICY_CHECK_eroute */#ifdef CONFIG_IPSEC_ALG	struct ipsec_alg_enc *ixt_e=NULL;	struct ipsec_alg_auth *ixt_a=NULL;#endif /* CONFIG_IPSEC_ALG */#ifdef CONFIG_IPSEC_NAT_TRAVERSAL	__u16 natt_len = 0, natt_sport = 0, natt_dport = 0;	__u8 natt_type = 0;#endif	/* Don't unlink in the middle of a turnaround */	MOD_INC_USE_COUNT;		if (skb == NULL) {		KLIPS_PRINT(debug_rcv, 			    "klips_debug:ipsec_rcv: "			    "NULL skb passed in.\n");		goto rcvleave;	}			if (skb->data == NULL) {		KLIPS_PRINT(debug_rcv,			    "klips_debug:ipsec_rcv: "			    "NULL skb->data passed in, packet is bogus, dropping.\n");		goto rcvleave;	}#ifdef CONFIG_IPSEC_NAT_TRAVERSAL	if (skb->sk && skb->nh.iph && skb->nh.iph->protocol==IPPROTO_UDP) {		/**		 * Packet comes from udp_queue_rcv_skb so it is already defrag,		 * checksum verified, ... (ie safe to use)		 *		 * If the packet is not for us, return -1 and udp_queue_rcv_skb		 * will continue to handle it (do not kfree skb !!).		 */		struct udp_opt *tp =  &(skb->sk->tp_pinfo.af_udp);		struct iphdr *ip = (struct iphdr *)skb->nh.iph;		struct udphdr *udp = (struct udphdr *)((__u32 *)ip+ip->ihl);		__u8 *udpdata = (__u8 *)udp + sizeof(struct udphdr);		__u32 *udpdata32 = (__u32 *)udpdata;		natt_sport = ntohs(udp->source);		natt_dport = ntohs(udp->dest);		KLIPS_PRINT(debug_rcv,		    "klips_debug:ipsec_rcv: "		    "suspected ESPinUDP packet (NAT-Traversal) [%d].\n",			tp->esp_in_udp);		KLIPS_IP_PRINT(debug_rcv, ip);		if (udpdata < skb->tail) {			unsigned int len = skb->tail - udpdata;			if ((len==1) && (udpdata[0]==0xff)) {				KLIPS_PRINT(debug_rcv,				    "klips_debug:ipsec_rcv: "					/* not IPv6 compliant message */				    "NAT-keepalive from %d.%d.%d.%d.\n", NIPQUAD(ip->saddr));				goto rcvleave;			}			else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_IKE) &&				(len > (2*sizeof(__u32) + sizeof(struct esp))) &&				(udpdata32[0]==0) && (udpdata32[1]==0) ) {				/* ESP Packet with Non-IKE header */				KLIPS_PRINT(debug_rcv, 					"klips_debug:ipsec_rcv: "					"ESPinUDP pkt with Non-IKE - spi=0x%x\n",					udpdata32[2]);				natt_type = ESPINUDP_WITH_NON_IKE;				natt_len = sizeof(struct udphdr)+(2*sizeof(__u32));			}			else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_ESP) &&				(len > sizeof(struct esp)) &&				(udpdata32[0]!=0) ) {				/* ESP Packet without Non-ESP header */				natt_type = ESPINUDP_WITH_NON_ESP;				natt_len = sizeof(struct udphdr);				KLIPS_PRINT(debug_rcv, 					"klips_debug:ipsec_rcv: "                                        "ESPinUDP pkt without Non-ESP - spi=0x%x\n",					udpdata32[0]);			}			else {				KLIPS_PRINT(debug_rcv,				    "klips_debug:ipsec_rcv: "					"IKE packet - not handled here\n");				MOD_DEC_USE_COUNT;				return -1;			}		}		else {			MOD_DEC_USE_COUNT;			return -1;		}	}#endif		#ifdef IPH_is_SKB_PULLED	/* In Linux 2.4.4, the IP header has been skb_pull()ed before the	   packet is passed to us. So we'll skb_push() to get back to it. */	if (skb->data == skb->h.raw) {		skb_push(skb, skb->h.raw - skb->nh.raw);	}#endif /* IPH_is_SKB_PULLED */	ipp = (struct iphdr *)skb->data;	iphlen = ipp->ihl << 2;	/* dev->hard_header_len is unreliable and should not be used */	hard_header_len = skb->mac.raw ? (skb->data - skb->mac.raw) : 0;	if((hard_header_len < 0) || (hard_header_len > skb_headroom(skb)))		hard_header_len = 0;#ifdef NET_21	/* if skb was cloned (most likely due to a packet sniffer such as	   tcpdump being momentarily attached to the interface), make	   a copy of our own to modify */	if(skb_cloned(skb)) {		/* include any mac header while copying.. */		if(skb_headroom(skb) < hard_header_len) {			printk(KERN_WARNING "klips_error:ipsec_rcv: "			       "tried to skb_push hhlen=%d, %d available.  This should never happen, please report.\n",			       hard_header_len,			       skb_headroom(skb));			goto rcvleave;		}		skb_push(skb, hard_header_len);		if#ifdef SKB_COW_NEW               (skb_cow(skb, skb_headroom(skb)) != 0)#else /* SKB_COW_NEW */               ((skb = skb_cow(skb, skb_headroom(skb))) == NULL)#endif /* SKB_COW_NEW */		{			goto rcvleave;		}		if(skb->len < hard_header_len) {			printk(KERN_WARNING "klips_error:ipsec_rcv: "			       "tried to skb_pull hhlen=%d, %d available.  This should never happen, please report.\n",			       hard_header_len,			       skb->len);			goto rcvleave;		}		skb_pull(skb, hard_header_len);	}	#endif /* NET_21 */		#if IP_FRAGMENT_LINEARIZE	/* In Linux 2.4.4, we may have to reassemble fragments. They are	   not assembled automatically to save TCP from having to copy	   twice.	*/      if (skb_is_nonlinear(skb)) {	if (skb_linearize(skb, GFP_ATOMIC) != 0) {	  goto rcvleave;	}      }      ipp = (struct iphdr *)skb->nh.iph;      iphlen = ipp->ihl << 2;#endif	#ifdef CONFIG_IPSEC_NAT_TRAVERSAL	if (natt_len) {		/**		 * Now, we are sure packet is ESPinUDP. Remove natt_len bytes from		 * packet and modify protocol to ESP.		 */		if (((unsigned char *)skb->data > (unsigned char *)skb->nh.iph) &&			((unsigned char *)skb->nh.iph > (unsigned char *)skb->head)) {			unsigned int _len = (unsigned char *)skb->data -				(unsigned char *)skb->nh.iph;			KLIPS_PRINT(debug_rcv,				"klips_debug:ipsec_rcv: adjusting skb: skb_push(%u)\n",				_len);			skb_push(skb, _len);		}		KLIPS_PRINT(debug_rcv,		    "klips_debug:ipsec_rcv: "			"removing %d bytes from ESPinUDP packet\n", natt_len);		ipp = (struct iphdr *)skb->data;		iphlen = ipp->ihl << 2;		ipp->tot_len = htons(ntohs(ipp->tot_len) - natt_len);		if (skb->len < iphlen + natt_len) {			printk(KERN_WARNING		       "klips_error:ipsec_rcv: "		       "ESPinUDP packet is too small (%d < %d+%d). "			   "This should never happen, please report.\n",		       (int)(skb->len), iphlen, natt_len);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?