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 + -
显示快捷键?