ipsec.c

来自「eCos操作系统源码」· C语言 代码 · 共 2,682 行 · 第 1/5 页

C
2,682
字号
//==========================================================================////      src/sys/netinet6/ipsec.c////==========================================================================//####BSDCOPYRIGHTBEGIN####//// -------------------------------------------//// Portions of this software may have been derived from OpenBSD, // FreeBSD or other sources, and are covered by the appropriate// copyright disclaimers included herein.//// Portions created by Red Hat are// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.//// -------------------------------------------////####BSDCOPYRIGHTEND####//==========================================================================/*	$KAME: ipsec.c,v 1.134 2001/12/03 09:08:47 jinmei Exp $	*//* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. *//* * IPsec controller part. */#include <sys/param.h>#include <sys/malloc.h>#include <sys/mbuf.h>#include <sys/domain.h>#include <sys/protosw.h>#include <sys/socket.h>#include <sys/socketvar.h>#include <sys/sysctl.h>#include <sys/errno.h>#include <sys/time.h>#include <net/if.h>#include <net/route.h>#include <netinet/in.h>#include <netinet/in_systm.h>#include <netinet/ip.h>#include <netinet/ip_var.h>#include <netinet/in_var.h>#include <netinet/udp.h>#include <netinet/udp_var.h>#include <netinet/ip_ecn.h>#include <netinet/tcp.h>#include <netinet/udp.h>#include <netinet/ip6.h>#ifdef INET6#include <netinet6/ip6_var.h>#endif#include <netinet/in_pcb.h>#ifdef INET6#if !((defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802))#include <netinet6/in6_pcb.h>#endif#include <netinet/icmp6.h>#endif#include <netinet6/ipsec.h>#include <netinet6/ah.h>#ifdef IPSEC_ESP#include <netinet6/esp.h>#endif#include <netinet6/ipcomp.h>#include <netkey/key.h>#include <netkey/keydb.h>#include <netkey/key_debug.h>#ifdef HAVE_NRL_INPCB#define in6pcb	inpcb#define in6p_sp	inp_sp#define in6p_socket	inp_socket#define sotoin6pcb(so)	((struct inpcb *)(so)->so_pcb)#endif#ifdef IPSEC_DEBUGint ipsec_debug = 1;#elseint ipsec_debug = 0;#endif/* 1 if we decapsulate tunnels by sec* device */#if defined(NSEC) && NSEC > 0#define USE_SEC_DEVICE	1#else#define USE_SEC_DEVICE	0#endifint ipsec_tunnel_device = USE_SEC_DEVICE;#ifndef offsetof		/* XXX */#define	offsetof(type, member)	((size_t)(&((type *)0)->member))#endifstruct ipsecstat ipsecstat;int ip4_ah_cleartos = 1;int ip4_ah_offsetmask = 0;	/* maybe IP_DF? */int ip4_ipsec_dfbit = 0;	/* DF bit on encap. 0: clear 1: set 2: copy */int ip4_esp_trans_deflev = IPSEC_LEVEL_USE;int ip4_esp_net_deflev = IPSEC_LEVEL_USE;int ip4_ah_trans_deflev = IPSEC_LEVEL_USE;int ip4_ah_net_deflev = IPSEC_LEVEL_USE;struct secpolicy ip4_def_policy;int ip4_ipsec_ecn = 0;		/* ECN ignore(-1)/forbidden(0)/allowed(1) */int ip4_esp_randpad = -1;static int sp_cachegen = 1;	/* cache generation # */#ifdef __FreeBSD__#ifdef SYSCTL_DECLSYSCTL_DECL(_net_inet_ipsec);#ifdef INET6SYSCTL_DECL(_net_inet6_ipsec6);#endif#endif/* net.inet.ipsec */SYSCTL_STRUCT(_net_inet_ipsec, IPSECCTL_STATS,	stats, CTLFLAG_RD,	&ipsecstat,	ipsecstat, "");SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY,	def_policy, CTLFLAG_RW,	&ip4_def_policy.policy,	0, "");SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev,	CTLFLAG_RW, &ip4_esp_trans_deflev,	0, "");SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev,	CTLFLAG_RW, &ip4_esp_net_deflev,	0, "");SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev,	CTLFLAG_RW, &ip4_ah_trans_deflev,	0, "");SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev,	CTLFLAG_RW, &ip4_ah_net_deflev,	0, "");SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS,	ah_cleartos, CTLFLAG_RW,	&ip4_ah_cleartos,	0, "");SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK,	ah_offsetmask, CTLFLAG_RW,	&ip4_ah_offsetmask,	0, "");SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DFBIT,	dfbit, CTLFLAG_RW,	&ip4_ipsec_dfbit,	0, "");SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ECN,	ecn, CTLFLAG_RW,	&ip4_ipsec_ecn,	0, "");SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEBUG,	debug, CTLFLAG_RW,	&ipsec_debug,	0, "");SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ESP_RANDPAD,	esp_randpad, CTLFLAG_RW,	&ip4_esp_randpad,	0, "");#endif /* __FreeBSD__ */#ifdef INET6struct ipsecstat ipsec6stat;int ip6_esp_trans_deflev = IPSEC_LEVEL_USE;int ip6_esp_net_deflev = IPSEC_LEVEL_USE;int ip6_ah_trans_deflev = IPSEC_LEVEL_USE;int ip6_ah_net_deflev = IPSEC_LEVEL_USE;struct secpolicy ip6_def_policy;int ip6_ipsec_ecn = 0;		/* ECN ignore(-1)/forbidden(0)/allowed(1) */int ip6_esp_randpad = -1;#ifdef __FreeBSD__/* net.inet6.ipsec6 */SYSCTL_STRUCT(_net_inet6_ipsec6, IPSECCTL_STATS,	stats, CTLFLAG_RD, &ipsec6stat, ipsecstat, "");SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY,	def_policy, CTLFLAG_RW,	&ip6_def_policy.policy,	0, "");SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev,	CTLFLAG_RW, &ip6_esp_trans_deflev,	0, "");SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev,	CTLFLAG_RW, &ip6_esp_net_deflev,	0, "");SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev,	CTLFLAG_RW, &ip6_ah_trans_deflev,	0, "");SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev,	CTLFLAG_RW, &ip6_ah_net_deflev,	0, "");SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ECN,	ecn, CTLFLAG_RW,	&ip6_ipsec_ecn,	0, "");SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG,	debug, CTLFLAG_RW,	&ipsec_debug,	0, "");SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ESP_RANDPAD,	esp_randpad, CTLFLAG_RW,	&ip6_esp_randpad,	0, "");#endif /* __FreeBSD__ */#endif /* INET6 */static struct secpolicy *ipsec_checkpcbcache __P((struct mbuf *,	struct inpcbpolicy *pcbsp, int));static int ipsec_fillpcbcache __P((struct inpcbpolicy *, struct mbuf *,	struct secpolicy *, int));static int ipsec_invalpcbcache __P((struct inpcbpolicy *, int));static int ipsec_setspidx_mbuf	__P((struct secpolicyindex *, u_int, u_int, struct mbuf *, int));static int ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *pcb));#ifdef INET6static int ipsec6_setspidx_in6pcb __P((struct mbuf *, struct in6pcb *pcb));#endifstatic int ipsec_setspidx __P((struct mbuf *, struct secpolicyindex *, int));static void ipsec4_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int));static int ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *));#ifdef INET6static void ipsec6_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int));static int ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *));#endifstatic struct inpcbpolicy *ipsec_newpcbpolicy __P((void));static void ipsec_delpcbpolicy __P((struct inpcbpolicy *));static struct secpolicy *ipsec_deepcopy_policy __P((struct secpolicy *src));static int ipsec_set_policy __P((struct secpolicy **pcb_sp,	int optname, caddr_t request, size_t len, int priv));static int ipsec_get_policy __P((struct secpolicy *pcb_sp, struct mbuf **mp));static void vshiftl __P((unsigned char *, int, int));static int ipsec_in_reject __P((struct secpolicy *, struct mbuf *));static size_t ipsec_hdrsiz __P((struct secpolicy *));#ifdef INETstatic struct mbuf *ipsec4_splithdr __P((struct mbuf *));#endif#ifdef INET6static struct mbuf *ipsec6_splithdr __P((struct mbuf *));#endif#ifdef INETstatic int ipsec4_encapsulate __P((struct mbuf *, struct secasvar *));#endif#ifdef INET6static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *));#endifstatic struct mbuf *ipsec_addaux __P((struct mbuf *));static struct mbuf *ipsec_findaux __P((struct mbuf *));static void ipsec_optaux __P((struct mbuf *, struct mbuf *));/* * try to validate and use cached policy on a pcb. */static struct secpolicy *ipsec_checkpcbcache(m, pcbsp, dir)	struct mbuf *m;	struct inpcbpolicy *pcbsp;	int dir;{	struct secpolicyindex spidx;#if defined(__FreeBSD__) && __FreeBSD__ > 2	struct timeval mono_time;	microtime(&mono_time);#endif	switch (dir) {	case IPSEC_DIR_INBOUND:	case IPSEC_DIR_OUTBOUND:		break;	default:		return NULL;	}#ifdef DIAGNOSTIC	if (dir >= sizeof(pcbsp->cache)/sizeof(pcbsp->cache[0]))		panic("dir too big in ipsec_checkpcbcache");#endif	/* SPD table change invalidates all the caches */	if (pcbsp->cachegen[dir] == 0 || sp_cachegen > pcbsp->cachegen[dir]) {		ipsec_invalpcbcache(pcbsp, dir);		return NULL;	}	if (!pcbsp->cache[dir])		return NULL;	if ((pcbsp->cacheflags & IPSEC_PCBSP_CONNECTED) == 0) {		if (!pcbsp->cache[dir])			return NULL;		if (ipsec_setspidx(m, &spidx, 1) != 0)			return NULL;		if (bcmp(&pcbsp->cacheidx[dir], &spidx, sizeof(spidx))) {			if (!key_cmpspidx_withmask(&pcbsp->cache[dir]->spidx,			    &spidx))				return NULL;			pcbsp->cacheidx[dir] = spidx;		}	} else {		/*		 * The pcb is connected, and the L4 code is sure that:		 * - outgoing side uses inp_[lf]addr		 * - incoming side looks up policy after inpcb lookup		 * and address pair is known to be stable.  We do not need		 * to generate spidx again, nor check the address match again.		 *		 * For IPv4/v6 SOCK_STREAM sockets, this assumption holds		 * and there are calls to ipsec_pcbconn() from in_pcbconnect().		 */	}	pcbsp->cache[dir]->lastused = mono_time.tv_sec;	pcbsp->cache[dir]->refcnt++;	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,		printf("DP ipsec_checkpcbcache cause refcnt++:%d SP:%p\n",		pcbsp->cache[dir]->refcnt, pcbsp->cache[dir]));	return pcbsp->cache[dir];}static intipsec_fillpcbcache(pcbsp, m, sp, dir)	struct inpcbpolicy *pcbsp;	struct mbuf *m;	struct secpolicy *sp;	int dir;{	switch (dir) {	case IPSEC_DIR_INBOUND:	case IPSEC_DIR_OUTBOUND:		break;	default:		return EINVAL;	}#ifdef DIAGNOSTIC	if (dir >= sizeof(pcbsp->cache)/sizeof(pcbsp->cache[0]))		panic("dir too big in ipsec_checkpcbcache");#endif	if (pcbsp->cache[dir])		key_freesp(pcbsp->cache[dir]);	pcbsp->cache[dir] = NULL;	if (ipsec_setspidx(m, &pcbsp->cacheidx[dir], 1) != 0) {		return EINVAL;	}	pcbsp->cache[dir] = sp;	if (pcbsp->cache[dir]) {		pcbsp->cache[dir]->refcnt++;		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,			printf("DP ipsec_fillpcbcache cause refcnt++:%d SP:%p\n",			pcbsp->cache[dir]->refcnt, pcbsp->cache[dir]));	}	pcbsp->cachegen[dir] = sp_cachegen;	return 0;}static intipsec_invalpcbcache(pcbsp, dir)	struct inpcbpolicy *pcbsp;	int dir;{	int i;	for (i = IPSEC_DIR_INBOUND; i <= IPSEC_DIR_OUTBOUND; i++) {		if (dir != IPSEC_DIR_ANY && i != dir)			continue;		if (pcbsp->cache[i])			key_freesp(pcbsp->cache[i]);		pcbsp->cache[i] = NULL;		pcbsp->cachegen[i] = 0;		bzero(&pcbsp->cacheidx[i], sizeof(pcbsp->cacheidx[i]));	}	return 0;}intipsec_pcbconn(pcbsp)	struct inpcbpolicy *pcbsp;{	pcbsp->cacheflags |= IPSEC_PCBSP_CONNECTED;	ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);	return 0;}intipsec_pcbdisconn(pcbsp)	struct inpcbpolicy *pcbsp;{	pcbsp->cacheflags &= ~IPSEC_PCBSP_CONNECTED;	ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);	return 0;}intipsec_invalpcbcacheall(){	sp_cachegen++;	return 0;}/* * For OUTBOUND packet having a socket. Searching SPD for packet, * and return a pointer to SP. * OUT:	NULL:	no apropreate SP found, the following value is set to error. *		0	: bypass *		EACCES	: discard packet. *		ENOENT	: ipsec_acquire() in progress, maybe. *		others	: error occured. *	others:	a pointer to SP * * NOTE: IPv6 mapped adddress concern is implemented here. */struct secpolicy *ipsec4_getpolicybysock(m, dir, so, error)	struct mbuf *m;	u_int dir;	struct socket *so;	int *error;{	struct inpcbpolicy *pcbsp = NULL;	struct secpolicy *currsp = NULL;	/* policy on socket */	struct secpolicy *kernsp = NULL;	/* policy on kernel */	/* sanity check */	if (m == NULL || so == NULL || error == NULL)		panic("ipsec4_getpolicybysock: NULL pointer was passed.\n");	switch (so->so_proto->pr_domain->dom_family) {	case AF_INET:		pcbsp = sotoinpcb(so)->inp_sp;		break;#ifdef INET6	case AF_INET6:		pcbsp = sotoin6pcb(so)->in6p_sp;		break;#endif	}	/* if we have a cached entry, and if it is still valid, use it. */	ipsecstat.spdcachelookup++;	currsp = ipsec_checkpcbcache(m, pcbsp, dir);	if (currsp) {		*error = 0;		return currsp;	}	ipsecstat.spdcachemiss++;	/*	 * XXX why is it necessary to do this, per-packet?	 * we are of course sure that the socket policy do match the packet,	 * because policy-on-pcb does not contain selectors, and	 * in_pcblookup (inbound) ensures the packet-pcb matching.	 */	switch (so->so_proto->pr_domain->dom_family) {	case AF_INET:		/* set spidx in pcb */		*error = ipsec4_setspidx_inpcb(m, sotoinpcb(so));		break;#ifdef INET6	case AF_INET6:		/* set spidx in pcb */		*error = ipsec6_setspidx_in6pcb(m, sotoin6pcb(so));		break;#endif	default:		panic("ipsec4_getpolicybysock: unsupported address family\n");	}	if (*error)		return NULL;	/* sanity check */	if (pcbsp == NULL)		panic("ipsec4_getpolicybysock: pcbsp is NULL.\n");	switch (dir) {	case IPSEC_DIR_INBOUND:		currsp = pcbsp->sp_in;		break;	case IPSEC_DIR_OUTBOUND:		currsp = pcbsp->sp_out;		break;	default:		panic("ipsec4_getpolicybysock: illegal direction.\n");	}	/* sanity check */	if (currsp == NULL)		panic("ipsec4_getpolicybysock: currsp is NULL.\n");	/* when privilieged socket */	if (pcbsp->priv) {		switch (currsp->policy) {		case IPSEC_POLICY_BYPASS:			currsp->refcnt++;			*error = 0;			ipsec_fillpcbcache(pcbsp, m, currsp, dir);			return currsp;		case IPSEC_POLICY_ENTRUST:			/* look for a policy in SPD */			kernsp = key_allocsp(&currsp->spidx, dir);			/* SP found */			if (kernsp != NULL) {				KEYDEBUG(KEYDEBUG_IPSEC_STAMP,					printf("DP ipsec4_getpolicybysock called "					       "to allocate SP:%p\n", kernsp));				*error = 0;				ipsec_fillpcbcache(pcbsp, m, kernsp, dir);				return kernsp;			}			/* no SP found */			if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD			 && ip4_def_policy.policy != IPSEC_POLICY_NONE) {				ipseclog((LOG_INFO,				    "fixed system default policy: %d->%d\n",				    ip4_def_policy.policy, IPSEC_POLICY_NONE));				ip4_def_policy.policy = IPSEC_POLICY_NONE;			}			ip4_def_policy.refcnt++;			*error = 0;			ipsec_fillpcbcache(pcbsp, m, &ip4_def_policy, dir);			return &ip4_def_policy;					case IPSEC_POLICY_IPSEC:			currsp->refcnt++;			*error = 0;			ipsec_fillpcbcache(pcbsp, m, currsp, dir);			return currsp;		default:

⌨️ 快捷键说明

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