2020-08-28 09:20:44 +03:00
|
|
|
/* $NetBSD: ipsec.c,v 1.172 2020/08/28 06:20:44 ozaki-r Exp $ */
|
2018-04-19 11:27:38 +03:00
|
|
|
/* $FreeBSD: ipsec.c,v 1.2.2.2 2003/07/01 01:38:13 sam Exp $ */
|
2018-02-16 14:07:44 +03:00
|
|
|
/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2007-02-10 12:43:05 +03:00
|
|
|
* notice, this list of conditions and the following disclaimer.
|
2003-08-14 00:06:49 +04:00
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
2007-02-10 12:43:05 +03:00
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
2003-08-14 00:06:49 +04:00
|
|
|
* 3. Neither the name of the project nor the names of its contributors
|
2007-02-10 12:43:05 +03:00
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
2003-08-14 00:06:49 +04:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2020-08-28 09:20:44 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: ipsec.c,v 1.172 2020/08/28 06:20:44 ozaki-r Exp $");
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* IPsec controller part.
|
|
|
|
*/
|
|
|
|
|
2017-04-06 12:20:07 +03:00
|
|
|
#if defined(_KERNEL_OPT)
|
2003-08-14 00:06:49 +04:00
|
|
|
#include "opt_inet.h"
|
|
|
|
#include "opt_ipsec.h"
|
2017-04-06 12:20:07 +03:00
|
|
|
#endif
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/domain.h>
|
|
|
|
#include <sys/protosw.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/socketvar.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/syslog.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/proc.h>
|
2009-05-10 06:13:07 +04:00
|
|
|
#include <sys/kauth.h>
|
2017-05-16 06:05:28 +03:00
|
|
|
#include <sys/cpu.h>
|
|
|
|
#include <sys/kmem.h>
|
2017-08-02 04:28:02 +03:00
|
|
|
#include <sys/pserialize.h>
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
#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/tcp.h>
|
|
|
|
#include <netinet/udp.h>
|
2008-06-27 09:18:58 +04:00
|
|
|
#include <netinet/ip_icmp.h>
|
2013-06-08 17:50:22 +04:00
|
|
|
#include <netinet/ip_private.h>
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
#include <netinet/ip6.h>
|
|
|
|
#ifdef INET6
|
|
|
|
#include <netinet6/ip6_var.h>
|
|
|
|
#endif
|
|
|
|
#include <netinet/in_pcb.h>
|
2018-07-11 08:25:45 +03:00
|
|
|
#include <netinet/in_offload.h>
|
2003-08-14 00:06:49 +04:00
|
|
|
#ifdef INET6
|
2004-01-21 01:55:14 +03:00
|
|
|
#include <netinet6/in6_pcb.h>
|
2003-08-14 00:06:49 +04:00
|
|
|
#include <netinet/icmp6.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <netipsec/ipsec.h>
|
2004-05-07 04:55:14 +04:00
|
|
|
#include <netipsec/ipsec_var.h>
|
2008-04-23 10:09:04 +04:00
|
|
|
#include <netipsec/ipsec_private.h>
|
2003-08-14 00:06:49 +04:00
|
|
|
#ifdef INET6
|
|
|
|
#include <netipsec/ipsec6.h>
|
|
|
|
#endif
|
|
|
|
#include <netipsec/ah_var.h>
|
|
|
|
#include <netipsec/esp_var.h>
|
|
|
|
#include <netipsec/ipcomp.h> /*XXX*/
|
|
|
|
#include <netipsec/ipcomp_var.h>
|
|
|
|
|
2003-10-07 02:05:15 +04:00
|
|
|
#include <netipsec/key.h>
|
|
|
|
#include <netipsec/keydb.h>
|
|
|
|
#include <netipsec/key_debug.h>
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
#include <netipsec/xform.h>
|
|
|
|
|
2014-05-30 05:39:03 +04:00
|
|
|
int ipsec_used = 0;
|
|
|
|
int ipsec_enabled = 1;
|
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
#ifdef IPSEC_DEBUG
|
|
|
|
int ipsec_debug = 1;
|
2006-04-12 00:21:28 +04:00
|
|
|
|
2018-02-16 14:07:44 +03:00
|
|
|
/*
|
2006-04-12 00:21:28 +04:00
|
|
|
* When set to 1, IPsec will send packets with the same sequence number.
|
|
|
|
* This allows to verify if the other side has proper replay attacks detection.
|
|
|
|
*/
|
|
|
|
int ipsec_replay = 0;
|
|
|
|
|
2018-02-16 14:07:44 +03:00
|
|
|
/*
|
2006-04-12 00:21:28 +04:00
|
|
|
* When set 1, IPsec will send packets with corrupted HMAC.
|
|
|
|
* This allows to verify if the other side properly detects modified packets.
|
|
|
|
*/
|
|
|
|
int ipsec_integrity = 0;
|
2003-08-14 00:06:49 +04:00
|
|
|
#else
|
|
|
|
int ipsec_debug = 0;
|
|
|
|
#endif
|
|
|
|
|
2008-04-23 10:09:04 +04:00
|
|
|
percpu_t *ipsecstat_percpu;
|
2018-02-16 14:07:44 +03:00
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
int ip4_ah_offsetmask = 0; /* maybe IP_DF? */
|
2005-10-05 16:59:24 +04:00
|
|
|
int ip4_ipsec_dfbit = 2; /* DF bit on encap. 0: clear 1: set 2: copy */
|
2003-08-14 00:06:49 +04:00
|
|
|
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) */
|
2004-03-02 05:22:56 +03:00
|
|
|
|
|
|
|
u_int ipsec_spdgen = 1; /* SPD generation # */
|
|
|
|
|
2017-09-19 05:44:14 +03:00
|
|
|
static struct secpolicy ipsec_dummy_sp __read_mostly = {
|
|
|
|
.state = IPSEC_SPSTATE_ALIVE,
|
|
|
|
/* If ENTRUST, the dummy SP never be used. See ipsec_getpolicybysock. */
|
|
|
|
.policy = IPSEC_POLICY_ENTRUST,
|
|
|
|
};
|
|
|
|
|
2018-02-16 14:07:44 +03:00
|
|
|
static struct secpolicy *ipsec_checkpcbcache(struct mbuf *,
|
|
|
|
struct inpcbpolicy *, int);
|
|
|
|
static int ipsec_fillpcbcache(struct inpcbpolicy *, struct mbuf *,
|
|
|
|
struct secpolicy *, int);
|
|
|
|
static int ipsec_invalpcbcache(struct inpcbpolicy *, int);
|
2004-03-02 05:22:56 +03:00
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
/*
|
|
|
|
* Crypto support requirements:
|
|
|
|
*
|
|
|
|
* 1 require hardware support
|
|
|
|
* -1 require software support
|
|
|
|
* 0 take anything
|
|
|
|
*/
|
2018-02-16 14:07:44 +03:00
|
|
|
int crypto_support = 0;
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2004-01-21 01:55:14 +03:00
|
|
|
static struct secpolicy *ipsec_getpolicybysock(struct mbuf *, u_int,
|
2017-04-19 06:39:14 +03:00
|
|
|
struct inpcb_hdr *, int *);
|
2004-01-21 01:55:14 +03:00
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
#ifdef INET6
|
|
|
|
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;
|
2004-01-21 01:55:14 +03:00
|
|
|
struct secpolicy ip6_def_policy;
|
2003-08-14 00:06:49 +04:00
|
|
|
int ip6_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */
|
2018-02-16 14:07:44 +03:00
|
|
|
#endif
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2018-02-28 13:16:19 +03:00
|
|
|
static int ipsec_setspidx_inpcb(struct mbuf *, void *);
|
2019-07-09 19:56:24 +03:00
|
|
|
static int ipsec_setspidx(struct mbuf *, struct secpolicyindex *, int, int);
|
2018-02-16 14:07:44 +03:00
|
|
|
static void ipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *, int);
|
|
|
|
static int ipsec4_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *);
|
2003-08-14 00:06:49 +04:00
|
|
|
#ifdef INET6
|
2018-02-16 14:07:44 +03:00
|
|
|
static void ipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *, int);
|
|
|
|
static int ipsec6_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *);
|
2003-08-14 00:06:49 +04:00
|
|
|
#endif
|
2018-02-16 14:07:44 +03:00
|
|
|
static void ipsec_delpcbpolicy(struct inpcbpolicy *);
|
2017-08-02 04:28:02 +03:00
|
|
|
static void ipsec_destroy_policy(struct secpolicy *);
|
2018-02-21 19:18:52 +03:00
|
|
|
static int ipsec_sp_reject(const struct secpolicy *, const struct mbuf *);
|
2018-02-16 14:07:44 +03:00
|
|
|
static void vshiftl(unsigned char *, int, int);
|
2018-02-21 19:18:52 +03:00
|
|
|
static size_t ipsec_sp_hdrsiz(const struct secpolicy *, const struct mbuf *);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2004-03-02 05:22:56 +03:00
|
|
|
/*
|
|
|
|
* Try to validate and use cached policy on a PCB.
|
|
|
|
*/
|
|
|
|
static struct secpolicy *
|
|
|
|
ipsec_checkpcbcache(struct mbuf *m, struct inpcbpolicy *pcbsp, int dir)
|
|
|
|
{
|
|
|
|
struct secpolicyindex spidx;
|
2017-08-02 04:28:02 +03:00
|
|
|
struct secpolicy *sp = NULL;
|
|
|
|
int s;
|
2004-03-02 05:22:56 +03:00
|
|
|
|
2017-04-20 06:41:47 +03:00
|
|
|
KASSERT(IPSEC_DIR_IS_VALID(dir));
|
2017-04-19 06:40:58 +03:00
|
|
|
KASSERT(pcbsp != NULL);
|
2017-05-23 12:08:45 +03:00
|
|
|
KASSERT(dir < __arraycount(pcbsp->sp_cache));
|
2017-04-25 08:44:11 +03:00
|
|
|
KASSERT(inph_locked(pcbsp->sp_inph));
|
2017-04-19 06:40:58 +03:00
|
|
|
|
2017-08-02 04:28:02 +03:00
|
|
|
/*
|
|
|
|
* Checking the generation and sp->state and taking a reference to an SP
|
|
|
|
* must be in a critical section of pserialize. See key_unlink_sp.
|
|
|
|
*/
|
|
|
|
s = pserialize_read_enter();
|
2004-03-02 05:22:56 +03:00
|
|
|
/* SPD table change invalidate all the caches. */
|
|
|
|
if (ipsec_spdgen != pcbsp->sp_cache[dir].cachegen) {
|
|
|
|
ipsec_invalpcbcache(pcbsp, dir);
|
2017-08-02 04:28:02 +03:00
|
|
|
goto out;
|
2004-03-02 05:22:56 +03:00
|
|
|
}
|
2017-08-02 04:28:02 +03:00
|
|
|
sp = pcbsp->sp_cache[dir].cachesp;
|
|
|
|
if (sp == NULL)
|
|
|
|
goto out;
|
|
|
|
if (sp->state != IPSEC_SPSTATE_ALIVE) {
|
|
|
|
sp = NULL;
|
2004-03-02 05:22:56 +03:00
|
|
|
ipsec_invalpcbcache(pcbsp, dir);
|
2017-08-02 04:28:02 +03:00
|
|
|
goto out;
|
2004-03-02 05:22:56 +03:00
|
|
|
}
|
|
|
|
if ((pcbsp->sp_cacheflags & IPSEC_PCBSP_CONNECTED) == 0) {
|
2017-08-02 04:28:02 +03:00
|
|
|
/* NB: assume ipsec_setspidx never sleep */
|
2019-07-09 19:56:24 +03:00
|
|
|
if (ipsec_setspidx(m, &spidx, dir, 1) != 0) {
|
2017-08-02 04:28:02 +03:00
|
|
|
sp = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
2007-03-25 16:46:42 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We have to make an exact match here since the cached rule
|
|
|
|
* might have lower priority than a rule that would otherwise
|
2018-02-16 14:07:44 +03:00
|
|
|
* have matched the packet.
|
2007-03-25 16:46:42 +04:00
|
|
|
*/
|
2017-08-02 04:28:02 +03:00
|
|
|
if (memcmp(&pcbsp->sp_cache[dir].cacheidx, &spidx,
|
|
|
|
sizeof(spidx))) {
|
|
|
|
sp = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
2004-03-02 05:22:56 +03:00
|
|
|
} 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 know to be stable. We do not need
|
|
|
|
* to generate spidx again, nor check the address match again.
|
|
|
|
*
|
|
|
|
* For IPv4/v6 SOCK_STREAM sockets, this assumptions holds
|
|
|
|
* and there are calls to ipsec_pcbconn() from in_pcbconnect().
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2017-08-02 04:28:02 +03:00
|
|
|
sp->lastused = time_second;
|
|
|
|
KEY_SP_REF(sp);
|
2017-04-19 06:42:11 +03:00
|
|
|
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
|
|
|
|
"DP cause refcnt++:%d SP:%p\n",
|
2017-08-02 04:28:02 +03:00
|
|
|
key_sp_refcnt(sp), pcbsp->sp_cache[dir].cachesp);
|
|
|
|
out:
|
|
|
|
pserialize_read_exit(s);
|
|
|
|
return sp;
|
2004-03-02 05:22:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ipsec_fillpcbcache(struct inpcbpolicy *pcbsp, struct mbuf *m,
|
2018-02-16 14:07:44 +03:00
|
|
|
struct secpolicy *sp, int dir)
|
2004-03-02 05:22:56 +03:00
|
|
|
{
|
|
|
|
|
2017-04-20 06:41:47 +03:00
|
|
|
KASSERT(IPSEC_DIR_IS_INOROUT(dir));
|
2017-05-23 12:08:45 +03:00
|
|
|
KASSERT(dir < __arraycount(pcbsp->sp_cache));
|
2017-04-25 08:44:11 +03:00
|
|
|
KASSERT(inph_locked(pcbsp->sp_inph));
|
2004-03-02 05:22:56 +03:00
|
|
|
|
|
|
|
pcbsp->sp_cache[dir].cachesp = NULL;
|
2017-06-02 06:39:28 +03:00
|
|
|
pcbsp->sp_cache[dir].cachehint = IPSEC_PCBHINT_UNKNOWN;
|
2019-07-09 19:56:24 +03:00
|
|
|
if (ipsec_setspidx(m, &pcbsp->sp_cache[dir].cacheidx, dir, 1) != 0) {
|
2004-03-02 05:22:56 +03:00
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
pcbsp->sp_cache[dir].cachesp = sp;
|
|
|
|
if (pcbsp->sp_cache[dir].cachesp) {
|
|
|
|
/*
|
|
|
|
* If the PCB is connected, we can remember a hint to
|
|
|
|
* possibly short-circuit IPsec processing in other places.
|
|
|
|
*/
|
|
|
|
if (pcbsp->sp_cacheflags & IPSEC_PCBSP_CONNECTED) {
|
|
|
|
switch (pcbsp->sp_cache[dir].cachesp->policy) {
|
|
|
|
case IPSEC_POLICY_NONE:
|
|
|
|
case IPSEC_POLICY_BYPASS:
|
|
|
|
pcbsp->sp_cache[dir].cachehint =
|
2017-06-14 05:00:43 +03:00
|
|
|
IPSEC_PCBHINT_NO;
|
2004-03-02 05:22:56 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pcbsp->sp_cache[dir].cachehint =
|
2017-06-14 05:00:43 +03:00
|
|
|
IPSEC_PCBHINT_YES;
|
2004-03-02 05:22:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pcbsp->sp_cache[dir].cachegen = ipsec_spdgen;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ipsec_invalpcbcache(struct inpcbpolicy *pcbsp, int dir)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2017-04-25 08:44:11 +03:00
|
|
|
KASSERT(inph_locked(pcbsp->sp_inph));
|
|
|
|
|
2004-03-02 05:22:56 +03:00
|
|
|
for (i = IPSEC_DIR_INBOUND; i <= IPSEC_DIR_OUTBOUND; i++) {
|
|
|
|
if (dir != IPSEC_DIR_ANY && i != dir)
|
|
|
|
continue;
|
|
|
|
pcbsp->sp_cache[i].cachesp = NULL;
|
2017-06-02 06:39:28 +03:00
|
|
|
pcbsp->sp_cache[i].cachehint = IPSEC_PCBHINT_UNKNOWN;
|
2004-03-02 05:22:56 +03:00
|
|
|
pcbsp->sp_cache[i].cachegen = 0;
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(&pcbsp->sp_cache[i].cacheidx, 0,
|
2017-06-14 05:00:43 +03:00
|
|
|
sizeof(pcbsp->sp_cache[i].cacheidx));
|
2004-03-02 05:22:56 +03:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ipsec_pcbconn(struct inpcbpolicy *pcbsp)
|
|
|
|
{
|
|
|
|
|
2017-04-25 08:44:11 +03:00
|
|
|
KASSERT(inph_locked(pcbsp->sp_inph));
|
|
|
|
|
2004-03-02 05:22:56 +03:00
|
|
|
pcbsp->sp_cacheflags |= IPSEC_PCBSP_CONNECTED;
|
|
|
|
ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ipsec_pcbdisconn(struct inpcbpolicy *pcbsp)
|
|
|
|
{
|
|
|
|
|
2017-04-25 08:44:11 +03:00
|
|
|
KASSERT(inph_locked(pcbsp->sp_inph));
|
|
|
|
|
2004-03-02 05:22:56 +03:00
|
|
|
pcbsp->sp_cacheflags &= ~IPSEC_PCBSP_CONNECTED;
|
|
|
|
ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ipsec_invalpcbcacheall(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (ipsec_spdgen == UINT_MAX)
|
|
|
|
ipsec_spdgen = 1;
|
|
|
|
else
|
|
|
|
ipsec_spdgen++;
|
|
|
|
}
|
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
/*
|
|
|
|
* Return a held reference to the default SP.
|
|
|
|
*/
|
|
|
|
static struct secpolicy *
|
2017-07-07 04:37:34 +03:00
|
|
|
key_get_default_sp(int af, const char *where, int tag)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
struct secpolicy *sp;
|
|
|
|
|
2017-04-19 06:42:11 +03:00
|
|
|
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, "DP from %s:%u\n", where, tag);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2017-04-19 06:43:34 +03:00
|
|
|
switch(af) {
|
|
|
|
case AF_INET:
|
|
|
|
sp = &ip4_def_policy;
|
|
|
|
break;
|
2007-04-15 18:17:12 +04:00
|
|
|
#ifdef INET6
|
2017-04-19 06:43:34 +03:00
|
|
|
case AF_INET6:
|
|
|
|
sp = &ip6_def_policy;
|
|
|
|
break;
|
2007-04-15 18:17:12 +04:00
|
|
|
#endif
|
2017-04-19 06:43:34 +03:00
|
|
|
default:
|
2017-04-19 06:42:11 +03:00
|
|
|
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
|
|
|
|
"unexpected protocol family %u\n", af);
|
2017-04-19 06:43:34 +03:00
|
|
|
return NULL;
|
|
|
|
}
|
2007-04-15 18:17:12 +04:00
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
if (sp->policy != IPSEC_POLICY_DISCARD &&
|
2018-02-21 19:18:52 +03:00
|
|
|
sp->policy != IPSEC_POLICY_NONE) {
|
2017-05-19 07:34:09 +03:00
|
|
|
IPSECLOG(LOG_INFO, "fixed system default policy: %d->%d\n",
|
|
|
|
sp->policy, IPSEC_POLICY_NONE);
|
2003-08-14 00:06:49 +04:00
|
|
|
sp->policy = IPSEC_POLICY_NONE;
|
|
|
|
}
|
2017-05-30 04:31:07 +03:00
|
|
|
KEY_SP_REF(sp);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2017-04-19 06:42:11 +03:00
|
|
|
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, "DP returns SP:%p (%u)\n",
|
2017-07-26 06:59:59 +03:00
|
|
|
sp, key_sp_refcnt(sp));
|
2003-08-14 00:06:49 +04:00
|
|
|
return sp;
|
|
|
|
}
|
2018-02-16 14:07:44 +03:00
|
|
|
|
2017-07-07 04:37:34 +03:00
|
|
|
#define KEY_GET_DEFAULT_SP(af) \
|
|
|
|
key_get_default_sp((af), __func__, __LINE__)
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For OUTBOUND packet having a socket. Searching SPD for packet,
|
|
|
|
* and return a pointer to SP.
|
2018-03-31 22:27:14 +03:00
|
|
|
* OUT: NULL: no appropriate SP found, the following value is set to error.
|
2003-08-14 00:06:49 +04:00
|
|
|
* 0 : bypass
|
|
|
|
* EACCES : discard packet.
|
|
|
|
* ENOENT : ipsec_acquire() in progress, maybe.
|
2004-02-24 18:12:51 +03:00
|
|
|
* others : error occurred.
|
2003-08-14 00:06:49 +04:00
|
|
|
* others: a pointer to SP
|
|
|
|
*
|
2006-02-25 05:28:55 +03:00
|
|
|
* NOTE: IPv6 mapped address concern is implemented here.
|
2003-08-14 00:06:49 +04:00
|
|
|
*/
|
2004-01-21 01:55:14 +03:00
|
|
|
static struct secpolicy *
|
2017-04-21 11:39:06 +03:00
|
|
|
ipsec_getpolicybysock(struct mbuf *m, u_int dir, struct inpcb_hdr *inph,
|
2017-04-19 06:39:14 +03:00
|
|
|
int *error)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
struct inpcbpolicy *pcbsp = NULL;
|
|
|
|
struct secpolicy *currsp = NULL; /* policy on socket */
|
|
|
|
struct secpolicy *sp;
|
|
|
|
int af;
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(m != NULL);
|
2017-04-21 11:39:06 +03:00
|
|
|
KASSERT(inph != NULL);
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(error != NULL);
|
2017-04-20 06:41:47 +03:00
|
|
|
KASSERTMSG(IPSEC_DIR_IS_INOROUT(dir), "invalid direction %u", dir);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2017-04-21 11:39:06 +03:00
|
|
|
KASSERT(inph->inph_socket != NULL);
|
2017-04-25 08:44:11 +03:00
|
|
|
KASSERT(inph_locked(inph));
|
2004-01-21 01:55:14 +03:00
|
|
|
|
2018-03-03 12:47:01 +03:00
|
|
|
/* XXX FIXME inpcb/in6pcb vs socket*/
|
2017-04-21 11:39:06 +03:00
|
|
|
af = inph->inph_af;
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERTMSG(af == AF_INET || af == AF_INET6,
|
|
|
|
"unexpected protocol family %u", af);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2017-04-21 11:39:06 +03:00
|
|
|
KASSERT(inph->inph_sp != NULL);
|
2004-03-02 05:22:56 +03:00
|
|
|
/* If we have a cached entry, and if it is still valid, use it. */
|
2008-04-23 10:09:04 +04:00
|
|
|
IPSEC_STATINC(IPSEC_STAT_SPDCACHELOOKUP);
|
2017-04-21 11:39:06 +03:00
|
|
|
currsp = ipsec_checkpcbcache(m, inph->inph_sp, dir);
|
2004-03-02 05:22:56 +03:00
|
|
|
if (currsp) {
|
|
|
|
*error = 0;
|
|
|
|
return currsp;
|
|
|
|
}
|
2008-04-23 10:09:04 +04:00
|
|
|
IPSEC_STATINC(IPSEC_STAT_SPDCACHEMISS);
|
2004-03-02 05:22:56 +03:00
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
switch (af) {
|
2018-03-03 12:47:01 +03:00
|
|
|
case AF_INET:
|
2004-01-21 01:55:14 +03:00
|
|
|
#if defined(INET6)
|
2018-03-03 12:47:01 +03:00
|
|
|
case AF_INET6:
|
2003-08-14 00:06:49 +04:00
|
|
|
#endif
|
2018-03-03 12:47:01 +03:00
|
|
|
*error = ipsec_setspidx_inpcb(m, inph);
|
|
|
|
pcbsp = inph->inph_sp;
|
|
|
|
break;
|
2003-08-14 00:06:49 +04:00
|
|
|
default:
|
|
|
|
*error = EPFNOSUPPORT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (*error)
|
|
|
|
return NULL;
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(pcbsp != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
switch (dir) {
|
|
|
|
case IPSEC_DIR_INBOUND:
|
|
|
|
currsp = pcbsp->sp_in;
|
|
|
|
break;
|
|
|
|
case IPSEC_DIR_OUTBOUND:
|
|
|
|
currsp = pcbsp->sp_out;
|
|
|
|
break;
|
|
|
|
}
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(currsp != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2018-03-03 12:54:55 +03:00
|
|
|
if (pcbsp->priv) { /* when privileged socket */
|
2003-08-14 00:06:49 +04:00
|
|
|
switch (currsp->policy) {
|
|
|
|
case IPSEC_POLICY_BYPASS:
|
|
|
|
case IPSEC_POLICY_IPSEC:
|
2017-05-30 04:31:07 +03:00
|
|
|
KEY_SP_REF(currsp);
|
2003-08-14 00:06:49 +04:00
|
|
|
sp = currsp;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPSEC_POLICY_ENTRUST:
|
|
|
|
/* look for a policy in SPD */
|
2019-08-07 13:10:00 +03:00
|
|
|
if (key_havesp(dir))
|
|
|
|
sp = KEY_LOOKUP_SP_BYSPIDX(&currsp->spidx, dir);
|
|
|
|
else
|
|
|
|
sp = NULL;
|
2003-08-14 00:06:49 +04:00
|
|
|
if (sp == NULL) /* no SP found */
|
2017-07-07 04:37:34 +03:00
|
|
|
sp = KEY_GET_DEFAULT_SP(af);
|
2003-08-14 00:06:49 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2017-05-19 07:34:09 +03:00
|
|
|
IPSECLOG(LOG_ERR, "Invalid policy for PCB %d\n",
|
|
|
|
currsp->policy);
|
2003-08-14 00:06:49 +04:00
|
|
|
*error = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else { /* unpriv, SPD has policy */
|
2019-08-07 13:10:00 +03:00
|
|
|
if (key_havesp(dir))
|
|
|
|
sp = KEY_LOOKUP_SP_BYSPIDX(&currsp->spidx, dir);
|
|
|
|
else
|
|
|
|
sp = NULL;
|
2003-08-14 00:06:49 +04:00
|
|
|
if (sp == NULL) { /* no SP found */
|
|
|
|
switch (currsp->policy) {
|
|
|
|
case IPSEC_POLICY_BYPASS:
|
2017-05-19 07:34:09 +03:00
|
|
|
IPSECLOG(LOG_ERR, "Illegal policy for "
|
|
|
|
"non-priviliged defined %d\n",
|
|
|
|
currsp->policy);
|
2003-08-14 00:06:49 +04:00
|
|
|
*error = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
case IPSEC_POLICY_ENTRUST:
|
2017-07-07 04:37:34 +03:00
|
|
|
sp = KEY_GET_DEFAULT_SP(af);
|
2003-08-14 00:06:49 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IPSEC_POLICY_IPSEC:
|
2017-05-30 04:31:07 +03:00
|
|
|
KEY_SP_REF(currsp);
|
2003-08-14 00:06:49 +04:00
|
|
|
sp = currsp;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2017-05-19 07:34:09 +03:00
|
|
|
IPSECLOG(LOG_ERR, "Invalid policy for "
|
|
|
|
"PCB %d\n", currsp->policy);
|
2003-08-14 00:06:49 +04:00
|
|
|
*error = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERTMSG(sp != NULL, "null SP (priv %u policy %u", pcbsp->priv,
|
|
|
|
currsp->policy);
|
2017-04-19 06:42:11 +03:00
|
|
|
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
|
|
|
|
"DP (priv %u policy %u) allocates SP:%p (refcnt %u)\n",
|
2017-07-26 06:59:59 +03:00
|
|
|
pcbsp->priv, currsp->policy, sp, key_sp_refcnt(sp));
|
2004-03-02 05:22:56 +03:00
|
|
|
ipsec_fillpcbcache(pcbsp, m, sp, dir);
|
2003-08-14 00:06:49 +04:00
|
|
|
return sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-02-21 19:18:52 +03:00
|
|
|
* For FORWARDING packet or OUTBOUND without a socket. Searching SPD for packet,
|
2003-08-14 00:06:49 +04:00
|
|
|
* and return a pointer to SP.
|
|
|
|
* OUT: positive: a pointer to the entry for security policy leaf matched.
|
2018-03-31 22:27:14 +03:00
|
|
|
* NULL: no appropriate SP found, the following value is set to error.
|
2003-08-14 00:06:49 +04:00
|
|
|
* 0 : bypass
|
|
|
|
* EACCES : discard packet.
|
|
|
|
* ENOENT : ipsec_acquire() in progress, maybe.
|
2004-02-24 18:12:51 +03:00
|
|
|
* others : error occurred.
|
2003-08-14 00:06:49 +04:00
|
|
|
*/
|
2018-02-16 18:18:41 +03:00
|
|
|
static struct secpolicy *
|
2007-07-07 22:38:22 +04:00
|
|
|
ipsec_getpolicybyaddr(struct mbuf *m, u_int dir, int flag, int *error)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
struct secpolicyindex spidx;
|
|
|
|
struct secpolicy *sp;
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(m != NULL);
|
|
|
|
KASSERT(error != NULL);
|
2017-04-20 06:41:47 +03:00
|
|
|
KASSERTMSG(IPSEC_DIR_IS_INOROUT(dir), "invalid direction %u", dir);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
sp = NULL;
|
|
|
|
|
2007-05-08 18:07:42 +04:00
|
|
|
/* Make an index to look for a policy. */
|
2019-07-09 19:56:24 +03:00
|
|
|
*error = ipsec_setspidx(m, &spidx, dir, (flag & IP_FORWARDING) ? 0 : 1);
|
2007-05-08 18:07:42 +04:00
|
|
|
if (*error != 0) {
|
2017-05-19 07:34:09 +03:00
|
|
|
IPSECLOG(LOG_DEBUG, "setpidx failed, dir %u flag %u\n", dir, flag);
|
2018-02-16 14:07:44 +03:00
|
|
|
memset(&spidx, 0, sizeof(spidx));
|
2007-05-08 18:07:42 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
spidx.dir = dir;
|
|
|
|
|
|
|
|
if (key_havesp(dir)) {
|
2017-07-07 04:37:34 +03:00
|
|
|
sp = KEY_LOOKUP_SP_BYSPIDX(&spidx, dir);
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|
2018-04-17 20:40:38 +03:00
|
|
|
if (sp == NULL) {
|
|
|
|
/* no SP found, use system default */
|
2017-07-07 04:37:34 +03:00
|
|
|
sp = KEY_GET_DEFAULT_SP(spidx.dst.sa.sa_family);
|
2018-04-17 20:40:38 +03:00
|
|
|
}
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(sp != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
return sp;
|
|
|
|
}
|
|
|
|
|
2018-02-16 18:18:41 +03:00
|
|
|
static struct secpolicy *
|
2018-02-26 11:42:16 +03:00
|
|
|
ipsec_checkpolicy(struct mbuf *m, u_int dir, u_int flag, int *error,
|
|
|
|
void *inp)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
struct secpolicy *sp;
|
|
|
|
|
|
|
|
*error = 0;
|
2004-01-21 01:55:14 +03:00
|
|
|
|
2017-04-20 11:46:07 +03:00
|
|
|
if (inp == NULL) {
|
2003-08-14 00:06:49 +04:00
|
|
|
sp = ipsec_getpolicybyaddr(m, dir, flag, error);
|
2017-04-20 11:46:07 +03:00
|
|
|
} else {
|
2018-02-21 19:38:15 +03:00
|
|
|
struct inpcb_hdr *inph = (struct inpcb_hdr *)inp;
|
|
|
|
KASSERT(inph->inph_socket != NULL);
|
|
|
|
sp = ipsec_getpolicybysock(m, dir, inph, error);
|
2017-04-20 11:46:07 +03:00
|
|
|
}
|
2003-08-14 00:06:49 +04:00
|
|
|
if (sp == NULL) {
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERTMSG(*error != 0, "getpolicy failed w/o error");
|
2008-04-23 10:09:04 +04:00
|
|
|
IPSEC_STATINC(IPSEC_STAT_OUT_INVAL);
|
2003-08-14 00:06:49 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERTMSG(*error == 0, "sp w/ error set to %u", *error);
|
2018-02-26 09:17:01 +03:00
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
switch (sp->policy) {
|
|
|
|
case IPSEC_POLICY_ENTRUST:
|
|
|
|
default:
|
2013-12-24 19:48:53 +04:00
|
|
|
printf("%s: invalid policy %u\n", __func__, sp->policy);
|
2003-08-14 00:06:49 +04:00
|
|
|
/* fall thru... */
|
|
|
|
case IPSEC_POLICY_DISCARD:
|
2008-04-23 10:09:04 +04:00
|
|
|
IPSEC_STATINC(IPSEC_STAT_OUT_POLVIO);
|
2003-08-14 00:06:49 +04:00
|
|
|
*error = -EINVAL; /* packet is discarded by caller */
|
|
|
|
break;
|
|
|
|
case IPSEC_POLICY_BYPASS:
|
|
|
|
case IPSEC_POLICY_NONE:
|
2017-08-02 04:28:02 +03:00
|
|
|
KEY_SP_UNREF(&sp);
|
2003-08-14 00:06:49 +04:00
|
|
|
sp = NULL; /* NB: force NULL result */
|
|
|
|
break;
|
|
|
|
case IPSEC_POLICY_IPSEC:
|
2017-05-23 07:26:08 +03:00
|
|
|
KASSERT(sp->req != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
break;
|
|
|
|
}
|
2018-02-26 09:17:01 +03:00
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
if (*error != 0) {
|
2017-08-02 04:28:02 +03:00
|
|
|
KEY_SP_UNREF(&sp);
|
2003-08-14 00:06:49 +04:00
|
|
|
sp = NULL;
|
2017-05-19 07:34:09 +03:00
|
|
|
IPSECLOG(LOG_DEBUG, "done, error %d\n", *error);
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|
2018-02-26 09:17:01 +03:00
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
return sp;
|
|
|
|
}
|
|
|
|
|
2013-06-08 07:26:05 +04:00
|
|
|
int
|
2017-03-03 10:13:06 +03:00
|
|
|
ipsec4_output(struct mbuf *m, struct inpcb *inp, int flags,
|
2020-08-28 09:19:13 +03:00
|
|
|
u_long *mtu, bool *natt_frag, bool *done, bool *count_drop)
|
2013-06-08 07:26:05 +04:00
|
|
|
{
|
|
|
|
struct secpolicy *sp = NULL;
|
2018-03-03 12:54:55 +03:00
|
|
|
u_long _mtu = 0;
|
2013-06-08 07:26:05 +04:00
|
|
|
int error, s;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the security policy (SP) for the packet and, if required,
|
|
|
|
* do IPsec-related processing. There are two cases here; the first
|
|
|
|
* time a packet is sent through it will be untagged and handled by
|
2018-02-26 11:42:16 +03:00
|
|
|
* ipsec_checkpolicy(). If the packet is resubmitted to ip_output
|
2013-06-08 07:26:05 +04:00
|
|
|
* (e.g. after AH, ESP, etc. processing), there will be a tag to
|
|
|
|
* bypass the lookup and related policy checking.
|
|
|
|
*/
|
|
|
|
if (ipsec_outdone(m)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
s = splsoftnet();
|
2017-06-02 06:41:20 +03:00
|
|
|
if (inp && ipsec_pcb_skip_ipsec(inp->inp_sp, IPSEC_DIR_OUTBOUND)) {
|
2013-06-08 07:26:05 +04:00
|
|
|
splx(s);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-02-26 11:42:16 +03:00
|
|
|
sp = ipsec_checkpolicy(m, IPSEC_DIR_OUTBOUND, flags, &error, inp);
|
2013-06-08 07:26:05 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* There are four return cases:
|
2018-03-03 12:54:55 +03:00
|
|
|
* sp != NULL apply IPsec policy
|
|
|
|
* sp == NULL, error == 0 no IPsec handling needed
|
|
|
|
* sp == NULL, error == -EINVAL discard packet w/o error
|
|
|
|
* sp == NULL, error != 0 discard packet, report error
|
2013-06-08 07:26:05 +04:00
|
|
|
*/
|
|
|
|
if (sp == NULL) {
|
|
|
|
splx(s);
|
|
|
|
if (error) {
|
|
|
|
/*
|
|
|
|
* Hack: -EINVAL is used to signal that a packet
|
|
|
|
* should be silently discarded. This is typically
|
|
|
|
* because we asked key management for an SA and
|
|
|
|
* it was delayed (e.g. kicked up to IKE).
|
|
|
|
*/
|
|
|
|
if (error == -EINVAL)
|
|
|
|
error = 0;
|
|
|
|
m_freem(m);
|
|
|
|
*done = true;
|
2020-08-28 09:19:13 +03:00
|
|
|
*count_drop = true;
|
2013-06-08 07:26:05 +04:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
/* No IPsec processing for this packet. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do delayed checksums now because we send before
|
|
|
|
* this is done in the normal processing path.
|
|
|
|
*/
|
|
|
|
if (m->m_pkthdr.csum_flags & (M_CSUM_TCPv4|M_CSUM_UDPv4)) {
|
2018-07-11 08:25:45 +03:00
|
|
|
in_undefer_cksum_tcpudp(m);
|
2013-06-08 07:26:05 +04:00
|
|
|
m->m_pkthdr.csum_flags &= ~(M_CSUM_TCPv4|M_CSUM_UDPv4);
|
|
|
|
}
|
|
|
|
|
2017-07-21 06:08:10 +03:00
|
|
|
error = ipsec4_process_packet(m, sp->req, &_mtu);
|
|
|
|
if (error == 0 && _mtu != 0) {
|
|
|
|
/*
|
|
|
|
* NAT-T ESP fragmentation: do not do IPSec processing
|
|
|
|
* now, we will do it on each fragmented packet.
|
|
|
|
*/
|
|
|
|
*mtu = _mtu;
|
|
|
|
*natt_frag = true;
|
2017-08-02 04:28:02 +03:00
|
|
|
KEY_SP_UNREF(&sp);
|
2017-07-21 06:08:10 +03:00
|
|
|
splx(s);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-02-26 09:17:01 +03:00
|
|
|
|
2013-06-08 07:26:05 +04:00
|
|
|
/*
|
|
|
|
* Preserve KAME behaviour: ENOENT can be returned
|
|
|
|
* when an SA acquire is in progress. Don't propagate
|
|
|
|
* this to user-level; it confuses applications.
|
|
|
|
*
|
|
|
|
* XXX this will go away when the SADB is redone.
|
|
|
|
*/
|
|
|
|
if (error == ENOENT)
|
|
|
|
error = 0;
|
2017-08-02 04:28:02 +03:00
|
|
|
KEY_SP_UNREF(&sp);
|
2013-06-08 07:26:05 +04:00
|
|
|
splx(s);
|
|
|
|
*done = true;
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2013-06-08 17:50:22 +04:00
|
|
|
int
|
2020-08-28 09:20:44 +03:00
|
|
|
ipsec_ip_input_checkpolicy(struct mbuf *m, bool forward)
|
2013-06-08 17:50:22 +04:00
|
|
|
{
|
|
|
|
struct secpolicy *sp;
|
|
|
|
int error, s;
|
|
|
|
|
|
|
|
s = splsoftnet();
|
2018-02-26 13:36:24 +03:00
|
|
|
error = ipsec_in_reject(m, NULL);
|
2013-06-08 17:50:22 +04:00
|
|
|
splx(s);
|
|
|
|
if (error) {
|
2018-02-26 13:36:24 +03:00
|
|
|
return EINVAL;
|
2013-06-08 17:50:22 +04:00
|
|
|
}
|
|
|
|
|
2018-05-14 20:34:26 +03:00
|
|
|
if (!forward || !(m->m_flags & M_CANFASTFWD)) {
|
2013-06-08 17:50:22 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-18 12:00:55 +03:00
|
|
|
/*
|
|
|
|
* Peek at the outbound SP for this packet to determine if
|
|
|
|
* it is a Fast Forward candidate.
|
|
|
|
*/
|
2013-06-08 17:50:22 +04:00
|
|
|
s = splsoftnet();
|
2018-05-14 20:34:26 +03:00
|
|
|
sp = ipsec_checkpolicy(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING,
|
|
|
|
&error, NULL);
|
2013-06-08 17:50:22 +04:00
|
|
|
if (sp != NULL) {
|
|
|
|
m->m_flags &= ~M_CANFASTFWD;
|
2017-08-02 04:28:02 +03:00
|
|
|
KEY_SP_UNREF(&sp);
|
2013-06-08 17:50:22 +04:00
|
|
|
}
|
|
|
|
splx(s);
|
2018-05-14 20:34:26 +03:00
|
|
|
|
2013-06-08 17:50:22 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-17 20:40:38 +03:00
|
|
|
/*
|
|
|
|
* If the packet is routed over IPsec tunnel, tell the originator the
|
|
|
|
* tunnel MTU.
|
|
|
|
* tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz
|
|
|
|
*
|
|
|
|
* XXX: Quick hack!!!
|
|
|
|
*
|
|
|
|
* XXX: And what if the MTU goes negative?
|
|
|
|
*/
|
2018-05-10 08:08:53 +03:00
|
|
|
void
|
|
|
|
ipsec_mtu(struct mbuf *m, int *destmtu)
|
2013-06-08 17:50:22 +04:00
|
|
|
{
|
|
|
|
struct secpolicy *sp;
|
|
|
|
size_t ipsechdr;
|
|
|
|
int error;
|
|
|
|
|
2018-02-26 09:17:01 +03:00
|
|
|
sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING,
|
|
|
|
&error);
|
2013-06-08 17:50:22 +04:00
|
|
|
if (sp == NULL) {
|
2018-05-10 08:08:53 +03:00
|
|
|
return;
|
2013-06-08 17:50:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Count IPsec header size. */
|
2018-02-27 11:05:19 +03:00
|
|
|
ipsechdr = ipsec_sp_hdrsiz(sp, m);
|
2013-06-08 17:50:22 +04:00
|
|
|
|
|
|
|
/*
|
2018-05-10 08:08:53 +03:00
|
|
|
* Find the correct route for outer IP header, compute tunnel MTU.
|
2013-06-08 17:50:22 +04:00
|
|
|
*/
|
2017-07-19 09:31:54 +03:00
|
|
|
if (sp->req) {
|
2017-10-03 11:25:21 +03:00
|
|
|
struct secasvar *sav;
|
|
|
|
|
|
|
|
sav = ipsec_lookup_sa(sp->req, m);
|
|
|
|
if (sav != NULL) {
|
|
|
|
struct route *ro;
|
|
|
|
struct rtentry *rt;
|
|
|
|
|
|
|
|
ro = &sav->sah->sa_route;
|
|
|
|
rt = rtcache_validate(ro);
|
|
|
|
if (rt && rt->rt_ifp) {
|
|
|
|
*destmtu = rt->rt_rmx.rmx_mtu ?
|
|
|
|
rt->rt_rmx.rmx_mtu : rt->rt_ifp->if_mtu;
|
|
|
|
*destmtu -= ipsechdr;
|
|
|
|
}
|
|
|
|
rtcache_unref(rt, ro);
|
|
|
|
KEY_SA_UNREF(&sav);
|
2013-06-08 17:50:22 +04:00
|
|
|
}
|
|
|
|
}
|
2017-08-02 04:28:02 +03:00
|
|
|
KEY_SP_UNREF(&sp);
|
2013-06-08 17:50:22 +04:00
|
|
|
}
|
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
static int
|
2018-02-28 13:16:19 +03:00
|
|
|
ipsec_setspidx_inpcb(struct mbuf *m, void *pcb)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
2018-02-28 13:16:19 +03:00
|
|
|
struct inpcb_hdr *inph = (struct inpcb_hdr *)pcb;
|
2003-08-14 00:06:49 +04:00
|
|
|
int error;
|
|
|
|
|
2018-02-28 13:16:19 +03:00
|
|
|
KASSERT(inph != NULL);
|
|
|
|
KASSERT(inph->inph_sp != NULL);
|
|
|
|
KASSERT(inph->inph_sp->sp_out != NULL);
|
|
|
|
KASSERT(inph->inph_sp->sp_in != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2019-07-09 19:56:24 +03:00
|
|
|
error = ipsec_setspidx(m, &inph->inph_sp->sp_in->spidx,
|
|
|
|
IPSEC_DIR_INBOUND, 1);
|
2018-02-28 13:09:17 +03:00
|
|
|
if (error == 0) {
|
2018-02-28 13:16:19 +03:00
|
|
|
inph->inph_sp->sp_out->spidx = inph->inph_sp->sp_in->spidx;
|
|
|
|
inph->inph_sp->sp_out->spidx.dir = IPSEC_DIR_OUTBOUND;
|
2018-02-28 13:09:17 +03:00
|
|
|
} else {
|
2018-02-28 13:16:19 +03:00
|
|
|
memset(&inph->inph_sp->sp_in->spidx, 0,
|
|
|
|
sizeof(inph->inph_sp->sp_in->spidx));
|
|
|
|
memset(&inph->inph_sp->sp_out->spidx, 0,
|
|
|
|
sizeof(inph->inph_sp->sp_out->spidx));
|
2018-02-28 13:09:17 +03:00
|
|
|
}
|
2003-08-14 00:06:49 +04:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* configure security policy index (src/dst/proto/sport/dport)
|
|
|
|
* by looking at the content of mbuf.
|
|
|
|
* the caller is responsible for error recovery (like clearing up spidx).
|
|
|
|
*/
|
|
|
|
static int
|
2019-07-09 19:56:24 +03:00
|
|
|
ipsec_setspidx(struct mbuf *m, struct secpolicyindex *spidx, int dir,
|
|
|
|
int needport)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
struct ip *ip = NULL;
|
|
|
|
struct ip ipbuf;
|
|
|
|
u_int v;
|
|
|
|
int error;
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(m != NULL);
|
2018-05-10 08:15:14 +03:00
|
|
|
M_VERIFY_PACKET(m);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
if (m->m_pkthdr.len < sizeof(struct ip)) {
|
2017-04-19 06:42:11 +03:00
|
|
|
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DUMP,
|
|
|
|
"pkthdr.len(%d) < sizeof(struct ip), ignored.\n",
|
|
|
|
m->m_pkthdr.len);
|
2003-08-14 00:06:49 +04:00
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-07-09 19:56:24 +03:00
|
|
|
memset(spidx, 0, sizeof(*spidx));
|
|
|
|
spidx->dir = dir;
|
|
|
|
|
2018-02-16 14:07:44 +03:00
|
|
|
if (m->m_len >= sizeof(*ip)) {
|
2003-08-14 00:06:49 +04:00
|
|
|
ip = mtod(m, struct ip *);
|
2018-02-16 14:07:44 +03:00
|
|
|
} else {
|
2007-03-05 00:17:54 +03:00
|
|
|
m_copydata(m, 0, sizeof(ipbuf), &ipbuf);
|
2003-08-14 00:06:49 +04:00
|
|
|
ip = &ipbuf;
|
|
|
|
}
|
|
|
|
v = ip->ip_v;
|
|
|
|
switch (v) {
|
|
|
|
case 4:
|
|
|
|
error = ipsec4_setspidx_ipaddr(m, spidx);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
ipsec4_get_ulp(m, spidx, needport);
|
|
|
|
return 0;
|
|
|
|
#ifdef INET6
|
|
|
|
case 6:
|
|
|
|
if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) {
|
2017-04-19 06:42:11 +03:00
|
|
|
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DUMP,
|
2013-12-24 19:48:53 +04:00
|
|
|
"pkthdr.len(%d) < sizeof(struct ip6_hdr), "
|
2017-04-19 06:42:11 +03:00
|
|
|
"ignored.\n", m->m_pkthdr.len);
|
2003-08-14 00:06:49 +04:00
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
error = ipsec6_setspidx_ipaddr(m, spidx);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
ipsec6_get_ulp(m, spidx, needport);
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
default:
|
2017-04-19 06:42:11 +03:00
|
|
|
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DUMP,
|
|
|
|
"unknown IP version %u, ignored.\n", v);
|
2003-08-14 00:06:49 +04:00
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport)
|
|
|
|
{
|
|
|
|
u_int8_t nxt;
|
|
|
|
int off;
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(m != NULL);
|
|
|
|
KASSERTMSG(m->m_pkthdr.len >= sizeof(struct ip), "packet too short");
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* NB: ip_input() flips it into host endian XXX need more checking */
|
2004-03-02 03:50:57 +03:00
|
|
|
if (m->m_len >= sizeof(struct ip)) {
|
2003-08-14 00:06:49 +04:00
|
|
|
struct ip *ip = mtod(m, struct ip *);
|
2017-04-19 06:39:14 +03:00
|
|
|
if (ip->ip_off & htons(IP_MF | IP_OFFMASK))
|
2003-08-14 00:06:49 +04:00
|
|
|
goto done;
|
|
|
|
off = ip->ip_hl << 2;
|
|
|
|
nxt = ip->ip_p;
|
|
|
|
} else {
|
|
|
|
struct ip ih;
|
|
|
|
|
2018-02-16 14:07:44 +03:00
|
|
|
m_copydata(m, 0, sizeof(struct ip), &ih);
|
2017-04-19 06:39:14 +03:00
|
|
|
if (ih.ip_off & htons(IP_MF | IP_OFFMASK))
|
2003-08-14 00:06:49 +04:00
|
|
|
goto done;
|
|
|
|
off = ih.ip_hl << 2;
|
|
|
|
nxt = ih.ip_p;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (off < m->m_pkthdr.len) {
|
|
|
|
struct ip6_ext ip6e;
|
|
|
|
struct tcphdr th;
|
|
|
|
struct udphdr uh;
|
2008-06-27 09:18:58 +04:00
|
|
|
struct icmp icmph;
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
switch (nxt) {
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
spidx->ul_proto = nxt;
|
|
|
|
if (!needport)
|
|
|
|
goto done_proto;
|
|
|
|
if (off + sizeof(struct tcphdr) > m->m_pkthdr.len)
|
|
|
|
goto done;
|
2018-02-16 14:07:44 +03:00
|
|
|
m_copydata(m, off, sizeof(th), &th);
|
2003-08-14 00:06:49 +04:00
|
|
|
spidx->src.sin.sin_port = th.th_sport;
|
|
|
|
spidx->dst.sin.sin_port = th.th_dport;
|
|
|
|
return;
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
spidx->ul_proto = nxt;
|
|
|
|
if (!needport)
|
|
|
|
goto done_proto;
|
|
|
|
if (off + sizeof(struct udphdr) > m->m_pkthdr.len)
|
|
|
|
goto done;
|
2018-02-16 14:07:44 +03:00
|
|
|
m_copydata(m, off, sizeof(uh), &uh);
|
2003-08-14 00:06:49 +04:00
|
|
|
spidx->src.sin.sin_port = uh.uh_sport;
|
|
|
|
spidx->dst.sin.sin_port = uh.uh_dport;
|
|
|
|
return;
|
|
|
|
case IPPROTO_AH:
|
2018-02-16 14:25:16 +03:00
|
|
|
if (off + sizeof(ip6e) > m->m_pkthdr.len)
|
2003-08-14 00:06:49 +04:00
|
|
|
goto done;
|
|
|
|
/* XXX sigh, this works but is totally bogus */
|
2007-03-05 00:17:54 +03:00
|
|
|
m_copydata(m, off, sizeof(ip6e), &ip6e);
|
2003-08-14 00:06:49 +04:00
|
|
|
off += (ip6e.ip6e_len + 2) << 2;
|
|
|
|
nxt = ip6e.ip6e_nxt;
|
|
|
|
break;
|
|
|
|
case IPPROTO_ICMP:
|
2008-06-27 09:18:58 +04:00
|
|
|
spidx->ul_proto = nxt;
|
|
|
|
if (off + sizeof(struct icmp) > m->m_pkthdr.len)
|
2018-02-21 19:42:33 +03:00
|
|
|
goto done;
|
2008-06-27 21:28:24 +04:00
|
|
|
m_copydata(m, off, sizeof(icmph), &icmph);
|
2008-06-27 09:18:58 +04:00
|
|
|
((struct sockaddr_in *)&spidx->src)->sin_port =
|
|
|
|
htons((uint16_t)icmph.icmp_type);
|
|
|
|
((struct sockaddr_in *)&spidx->dst)->sin_port =
|
|
|
|
htons((uint16_t)icmph.icmp_code);
|
|
|
|
return;
|
2003-08-14 00:06:49 +04:00
|
|
|
default:
|
|
|
|
/* XXX intermediate headers??? */
|
|
|
|
spidx->ul_proto = nxt;
|
|
|
|
goto done_proto;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
spidx->ul_proto = IPSEC_ULPROTO_ANY;
|
|
|
|
done_proto:
|
|
|
|
spidx->src.sin.sin_port = IPSEC_PORT_ANY;
|
|
|
|
spidx->dst.sin.sin_port = IPSEC_PORT_ANY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ipsec4_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx)
|
|
|
|
{
|
|
|
|
static const struct sockaddr_in template = {
|
2018-02-16 14:07:44 +03:00
|
|
|
sizeof(struct sockaddr_in),
|
2003-08-14 00:06:49 +04:00
|
|
|
AF_INET,
|
|
|
|
0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
spidx->src.sin = template;
|
|
|
|
spidx->dst.sin = template;
|
|
|
|
|
2018-02-16 14:07:44 +03:00
|
|
|
if (m->m_len < sizeof(struct ip)) {
|
2003-08-14 00:06:49 +04:00
|
|
|
m_copydata(m, offsetof(struct ip, ip_src),
|
2017-06-14 05:00:43 +03:00
|
|
|
sizeof(struct in_addr), &spidx->src.sin.sin_addr);
|
2003-08-14 00:06:49 +04:00
|
|
|
m_copydata(m, offsetof(struct ip, ip_dst),
|
2017-06-14 05:00:43 +03:00
|
|
|
sizeof(struct in_addr), &spidx->dst.sin.sin_addr);
|
2003-08-14 00:06:49 +04:00
|
|
|
} else {
|
|
|
|
struct ip *ip = mtod(m, struct ip *);
|
|
|
|
spidx->src.sin.sin_addr = ip->ip_src;
|
|
|
|
spidx->dst.sin.sin_addr = ip->ip_dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
spidx->prefs = sizeof(struct in_addr) << 3;
|
|
|
|
spidx->prefd = sizeof(struct in_addr) << 3;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef INET6
|
|
|
|
static void
|
2018-02-16 14:07:44 +03:00
|
|
|
ipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
int off, nxt;
|
|
|
|
struct tcphdr th;
|
|
|
|
struct udphdr uh;
|
2008-06-27 09:18:58 +04:00
|
|
|
struct icmp6_hdr icmph;
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2017-04-19 10:19:46 +03:00
|
|
|
KASSERT(m != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2017-04-19 06:42:11 +03:00
|
|
|
if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DUMP)) {
|
2017-09-28 20:21:42 +03:00
|
|
|
kdebug_mbuf(__func__, m);
|
2017-04-19 06:42:11 +03:00
|
|
|
}
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* set default */
|
|
|
|
spidx->ul_proto = IPSEC_ULPROTO_ANY;
|
|
|
|
((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY;
|
|
|
|
((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY;
|
|
|
|
|
|
|
|
nxt = -1;
|
|
|
|
off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
|
|
|
|
if (off < 0 || m->m_pkthdr.len < off)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (nxt) {
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
spidx->ul_proto = nxt;
|
|
|
|
if (!needport)
|
|
|
|
break;
|
|
|
|
if (off + sizeof(struct tcphdr) > m->m_pkthdr.len)
|
|
|
|
break;
|
2007-03-05 00:17:54 +03:00
|
|
|
m_copydata(m, off, sizeof(th), &th);
|
2003-08-14 00:06:49 +04:00
|
|
|
((struct sockaddr_in6 *)&spidx->src)->sin6_port = th.th_sport;
|
|
|
|
((struct sockaddr_in6 *)&spidx->dst)->sin6_port = th.th_dport;
|
|
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
spidx->ul_proto = nxt;
|
|
|
|
if (!needport)
|
|
|
|
break;
|
|
|
|
if (off + sizeof(struct udphdr) > m->m_pkthdr.len)
|
|
|
|
break;
|
2007-03-05 00:17:54 +03:00
|
|
|
m_copydata(m, off, sizeof(uh), &uh);
|
2003-08-14 00:06:49 +04:00
|
|
|
((struct sockaddr_in6 *)&spidx->src)->sin6_port = uh.uh_sport;
|
|
|
|
((struct sockaddr_in6 *)&spidx->dst)->sin6_port = uh.uh_dport;
|
|
|
|
break;
|
|
|
|
case IPPROTO_ICMPV6:
|
2008-06-27 09:18:58 +04:00
|
|
|
spidx->ul_proto = nxt;
|
|
|
|
if (off + sizeof(struct icmp6_hdr) > m->m_pkthdr.len)
|
|
|
|
break;
|
2008-06-27 21:28:24 +04:00
|
|
|
m_copydata(m, off, sizeof(icmph), &icmph);
|
2008-06-27 09:18:58 +04:00
|
|
|
((struct sockaddr_in6 *)&spidx->src)->sin6_port =
|
|
|
|
htons((uint16_t)icmph.icmp6_type);
|
|
|
|
((struct sockaddr_in6 *)&spidx->dst)->sin6_port =
|
|
|
|
htons((uint16_t)icmph.icmp6_code);
|
|
|
|
break;
|
2003-08-14 00:06:49 +04:00
|
|
|
default:
|
|
|
|
/* XXX intermediate headers??? */
|
|
|
|
spidx->ul_proto = nxt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-07-07 22:38:22 +04:00
|
|
|
ipsec6_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
struct ip6_hdr *ip6 = NULL;
|
|
|
|
struct ip6_hdr ip6buf;
|
|
|
|
struct sockaddr_in6 *sin6;
|
|
|
|
|
2018-04-17 20:40:38 +03:00
|
|
|
if (m->m_len >= sizeof(*ip6)) {
|
2003-08-14 00:06:49 +04:00
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
2018-04-17 20:40:38 +03:00
|
|
|
} else {
|
2007-03-05 00:17:54 +03:00
|
|
|
m_copydata(m, 0, sizeof(ip6buf), &ip6buf);
|
2003-08-14 00:06:49 +04:00
|
|
|
ip6 = &ip6buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
sin6 = (struct sockaddr_in6 *)&spidx->src;
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(sin6, 0, sizeof(*sin6));
|
2003-08-14 00:06:49 +04:00
|
|
|
sin6->sin6_family = AF_INET6;
|
|
|
|
sin6->sin6_len = sizeof(struct sockaddr_in6);
|
2009-04-18 18:58:02 +04:00
|
|
|
memcpy(&sin6->sin6_addr, &ip6->ip6_src, sizeof(ip6->ip6_src));
|
2003-08-14 00:06:49 +04:00
|
|
|
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
|
|
|
|
sin6->sin6_addr.s6_addr16[1] = 0;
|
|
|
|
sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
|
|
|
|
}
|
|
|
|
spidx->prefs = sizeof(struct in6_addr) << 3;
|
|
|
|
|
|
|
|
sin6 = (struct sockaddr_in6 *)&spidx->dst;
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(sin6, 0, sizeof(*sin6));
|
2003-08-14 00:06:49 +04:00
|
|
|
sin6->sin6_family = AF_INET6;
|
|
|
|
sin6->sin6_len = sizeof(struct sockaddr_in6);
|
2009-04-18 18:58:02 +04:00
|
|
|
memcpy(&sin6->sin6_addr, &ip6->ip6_dst, sizeof(ip6->ip6_dst));
|
2003-08-14 00:06:49 +04:00
|
|
|
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
|
|
|
|
sin6->sin6_addr.s6_addr16[1] = 0;
|
|
|
|
sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
|
|
|
|
}
|
|
|
|
spidx->prefd = sizeof(struct in6_addr) << 3;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
2007-07-07 22:38:22 +04:00
|
|
|
ipsec_delpcbpolicy(struct inpcbpolicy *p)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
2017-05-16 06:05:28 +03:00
|
|
|
|
2017-05-16 10:25:57 +03:00
|
|
|
kmem_intr_free(p, sizeof(*p));
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2018-04-28 17:21:03 +03:00
|
|
|
ipsec_init_pcbpolicy(struct socket *so, struct inpcbpolicy **policy)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
struct inpcbpolicy *new;
|
|
|
|
|
2017-04-19 10:19:46 +03:00
|
|
|
KASSERT(so != NULL);
|
|
|
|
KASSERT(policy != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2017-05-16 10:25:57 +03:00
|
|
|
new = kmem_intr_zalloc(sizeof(*new), KM_NOSLEEP);
|
|
|
|
if (new == NULL) {
|
2017-05-19 07:34:09 +03:00
|
|
|
IPSECLOG(LOG_DEBUG, "No more memory.\n");
|
2017-05-16 10:25:57 +03:00
|
|
|
return ENOBUFS;
|
|
|
|
}
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
if (IPSEC_PRIVILEGED_SO(so))
|
|
|
|
new->priv = 1;
|
|
|
|
else
|
|
|
|
new->priv = 0;
|
|
|
|
|
2017-08-02 04:28:02 +03:00
|
|
|
/*
|
2017-09-19 05:44:14 +03:00
|
|
|
* Set dummy SPs. Actual SPs will be allocated later if needed.
|
2017-08-02 04:28:02 +03:00
|
|
|
*/
|
2017-09-19 05:44:14 +03:00
|
|
|
new->sp_in = &ipsec_dummy_sp;
|
|
|
|
new->sp_out = &ipsec_dummy_sp;
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2012-12-07 19:29:38 +04:00
|
|
|
*policy = new;
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-08-02 04:28:02 +03:00
|
|
|
static void
|
|
|
|
ipsec_destroy_policy(struct secpolicy *sp)
|
|
|
|
{
|
|
|
|
|
2018-04-17 20:40:38 +03:00
|
|
|
if (sp == &ipsec_dummy_sp) {
|
2017-09-19 05:44:14 +03:00
|
|
|
; /* It's dummy. No need to free it. */
|
2018-04-17 20:40:38 +03:00
|
|
|
} else {
|
2017-08-02 04:28:02 +03:00
|
|
|
/*
|
|
|
|
* We cannot destroy here because it can be called in
|
|
|
|
* softint. So mark the SP as DEAD and let the timer
|
|
|
|
* destroy it. See key_timehandler_spd.
|
|
|
|
*/
|
|
|
|
sp->state = IPSEC_SPSTATE_DEAD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-27 18:01:30 +03:00
|
|
|
int
|
2018-04-29 14:51:08 +03:00
|
|
|
ipsec_set_policy(void *inp, const void *request, size_t len,
|
2018-02-27 18:01:30 +03:00
|
|
|
kauth_cred_t cred)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
2018-02-27 18:01:30 +03:00
|
|
|
struct inpcb_hdr *inph = (struct inpcb_hdr *)inp;
|
2011-06-09 23:54:18 +04:00
|
|
|
const struct sadb_x_policy *xpl;
|
2018-02-27 17:52:51 +03:00
|
|
|
struct secpolicy *newsp, *oldsp;
|
2018-02-27 18:01:30 +03:00
|
|
|
struct secpolicy **policy;
|
2003-08-14 00:06:49 +04:00
|
|
|
int error;
|
|
|
|
|
2017-05-16 06:05:28 +03:00
|
|
|
KASSERT(!cpu_softintr_p());
|
2018-02-27 18:01:30 +03:00
|
|
|
KASSERT(inph != NULL);
|
|
|
|
KASSERT(inph_locked(inph));
|
|
|
|
KASSERT(request != NULL);
|
|
|
|
|
|
|
|
if (len < sizeof(*xpl))
|
|
|
|
return EINVAL;
|
|
|
|
xpl = (const struct sadb_x_policy *)request;
|
|
|
|
|
|
|
|
KASSERT(inph->inph_sp != NULL);
|
|
|
|
|
|
|
|
/* select direction */
|
|
|
|
switch (xpl->sadb_x_policy_dir) {
|
|
|
|
case IPSEC_DIR_INBOUND:
|
|
|
|
policy = &inph->inph_sp->sp_in;
|
|
|
|
break;
|
|
|
|
case IPSEC_DIR_OUTBOUND:
|
|
|
|
policy = &inph->inph_sp->sp_out;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
IPSECLOG(LOG_ERR, "invalid direction=%u\n",
|
|
|
|
xpl->sadb_x_policy_dir);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2017-05-16 06:05:28 +03:00
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
/* sanity check. */
|
2018-02-27 17:52:51 +03:00
|
|
|
if (policy == NULL || *policy == NULL)
|
2003-08-14 00:06:49 +04:00
|
|
|
return EINVAL;
|
|
|
|
|
2017-04-19 06:42:11 +03:00
|
|
|
if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DUMP)) {
|
2017-09-28 20:21:42 +03:00
|
|
|
kdebug_sadb_xpolicy("set passed policy", request);
|
2017-04-19 06:42:11 +03:00
|
|
|
}
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* check policy type */
|
|
|
|
/* ipsec_set_policy() accepts IPSEC, ENTRUST and BYPASS. */
|
2018-02-16 14:07:44 +03:00
|
|
|
if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD ||
|
|
|
|
xpl->sadb_x_policy_type == IPSEC_POLICY_NONE)
|
2003-08-14 00:06:49 +04:00
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
/* check privileged socket */
|
2009-05-10 06:13:07 +04:00
|
|
|
if (xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS) {
|
2012-03-13 22:40:26 +04:00
|
|
|
error = kauth_authorize_network(cred, KAUTH_NETWORK_IPSEC,
|
|
|
|
KAUTH_REQ_NETWORK_IPSEC_BYPASS, NULL, NULL, NULL);
|
2009-05-10 06:13:07 +04:00
|
|
|
if (error)
|
2018-02-16 14:07:44 +03:00
|
|
|
return error;
|
2009-05-10 06:13:07 +04:00
|
|
|
}
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* allocation new SP entry */
|
|
|
|
if ((newsp = key_msg2sp(xpl, len, &error)) == NULL)
|
|
|
|
return error;
|
|
|
|
|
2017-08-02 04:28:02 +03:00
|
|
|
key_init_sp(newsp);
|
|
|
|
newsp->created = time_uptime;
|
|
|
|
/* Insert the global list for SPs for sockets */
|
|
|
|
key_socksplist_add(newsp);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* clear old SP and set new SP */
|
2017-08-02 04:28:02 +03:00
|
|
|
oldsp = *policy;
|
2012-12-07 19:29:38 +04:00
|
|
|
*policy = newsp;
|
2017-08-02 04:28:02 +03:00
|
|
|
ipsec_destroy_policy(oldsp);
|
|
|
|
|
2017-04-19 06:42:11 +03:00
|
|
|
if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DUMP)) {
|
|
|
|
printf("%s: new policy\n", __func__);
|
|
|
|
kdebug_secpolicy(newsp);
|
|
|
|
}
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2018-02-27 17:44:10 +03:00
|
|
|
ipsec_get_policy(void *inp, const void *request, size_t len,
|
2018-02-16 14:07:44 +03:00
|
|
|
struct mbuf **mp)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
2018-02-27 16:36:21 +03:00
|
|
|
struct inpcb_hdr *inph = (struct inpcb_hdr *)inp;
|
2011-06-09 23:54:18 +04:00
|
|
|
const struct sadb_x_policy *xpl;
|
2012-12-07 19:29:38 +04:00
|
|
|
struct secpolicy *policy;
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* sanity check. */
|
2018-02-27 16:36:21 +03:00
|
|
|
if (inph == NULL || request == NULL || mp == NULL)
|
2003-08-14 00:06:49 +04:00
|
|
|
return EINVAL;
|
2018-02-27 16:36:21 +03:00
|
|
|
KASSERT(inph->inph_sp != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
if (len < sizeof(*xpl))
|
|
|
|
return EINVAL;
|
2011-06-09 23:54:18 +04:00
|
|
|
xpl = (const struct sadb_x_policy *)request;
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* select direction */
|
|
|
|
switch (xpl->sadb_x_policy_dir) {
|
|
|
|
case IPSEC_DIR_INBOUND:
|
2018-02-27 16:36:21 +03:00
|
|
|
policy = inph->inph_sp->sp_in;
|
2003-08-14 00:06:49 +04:00
|
|
|
break;
|
|
|
|
case IPSEC_DIR_OUTBOUND:
|
2018-02-27 16:36:21 +03:00
|
|
|
policy = inph->inph_sp->sp_out;
|
2003-08-14 00:06:49 +04:00
|
|
|
break;
|
|
|
|
default:
|
2017-05-19 07:34:09 +03:00
|
|
|
IPSECLOG(LOG_ERR, "invalid direction=%u\n",
|
|
|
|
xpl->sadb_x_policy_dir);
|
2003-08-14 00:06:49 +04:00
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-02-27 17:52:51 +03:00
|
|
|
if (policy == NULL)
|
2018-02-27 17:44:10 +03:00
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
*mp = key_sp2msg(policy, M_NOWAIT);
|
|
|
|
if (!*mp) {
|
|
|
|
IPSECLOG(LOG_DEBUG, "No more memory.\n");
|
|
|
|
return ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DUMP)) {
|
|
|
|
kdebug_mbuf(__func__, *mp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2018-02-27 17:44:10 +03:00
|
|
|
ipsec_delete_pcbpolicy(void *inp)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
2018-02-27 16:36:21 +03:00
|
|
|
struct inpcb_hdr *inph = (struct inpcb_hdr *)inp;
|
2017-04-18 08:26:41 +03:00
|
|
|
|
2018-02-27 16:36:21 +03:00
|
|
|
KASSERT(inph != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2018-02-27 16:36:21 +03:00
|
|
|
if (inph->inph_sp == NULL)
|
2003-08-14 00:06:49 +04:00
|
|
|
return 0;
|
|
|
|
|
2018-02-27 16:36:21 +03:00
|
|
|
if (inph->inph_sp->sp_in != NULL)
|
|
|
|
ipsec_destroy_policy(inph->inph_sp->sp_in);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2018-02-27 16:36:21 +03:00
|
|
|
if (inph->inph_sp->sp_out != NULL)
|
|
|
|
ipsec_destroy_policy(inph->inph_sp->sp_out);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2018-02-27 16:36:21 +03:00
|
|
|
ipsec_invalpcbcache(inph->inph_sp, IPSEC_DIR_ANY);
|
2011-02-11 20:53:35 +03:00
|
|
|
|
2018-02-27 16:36:21 +03:00
|
|
|
ipsec_delpcbpolicy(inph->inph_sp);
|
|
|
|
inph->inph_sp = NULL;
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-02-26 09:17:01 +03:00
|
|
|
* Return the current level (either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE).
|
2003-08-14 00:06:49 +04:00
|
|
|
*/
|
|
|
|
u_int
|
2011-06-05 05:40:40 +04:00
|
|
|
ipsec_get_reqlevel(const struct ipsecrequest *isr)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
u_int level = 0;
|
|
|
|
u_int esp_trans_deflev, esp_net_deflev;
|
|
|
|
u_int ah_trans_deflev, ah_net_deflev;
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(isr != NULL);
|
|
|
|
KASSERT(isr->sp != NULL);
|
|
|
|
KASSERTMSG(
|
|
|
|
isr->sp->spidx.src.sa.sa_family == isr->sp->spidx.dst.sa.sa_family,
|
|
|
|
"af family mismatch, src %u, dst %u",
|
|
|
|
isr->sp->spidx.src.sa.sa_family, isr->sp->spidx.dst.sa.sa_family);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* XXX note that we have ipseclog() expanded here - code sync issue */
|
2018-02-26 09:17:01 +03:00
|
|
|
#define IPSEC_CHECK_DEFAULT(lev) \
|
2013-12-24 19:48:53 +04:00
|
|
|
(((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \
|
|
|
|
&& (lev) != IPSEC_LEVEL_UNIQUE) ? \
|
|
|
|
(ipsec_debug ? log(LOG_INFO, "fixed system default level " #lev \
|
2014-08-13 23:43:47 +04:00
|
|
|
":%d->%d\n", (lev), IPSEC_LEVEL_REQUIRE) : (void)0), \
|
2013-12-24 19:48:53 +04:00
|
|
|
(lev) = IPSEC_LEVEL_REQUIRE, (lev) \
|
|
|
|
: (lev))
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* set default level */
|
|
|
|
switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
|
|
|
esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_trans_deflev);
|
|
|
|
esp_net_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_net_deflev);
|
|
|
|
ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_trans_deflev);
|
|
|
|
ah_net_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_net_deflev);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef INET6
|
|
|
|
case AF_INET6:
|
|
|
|
esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_trans_deflev);
|
|
|
|
esp_net_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_net_deflev);
|
|
|
|
ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_trans_deflev);
|
|
|
|
ah_net_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_net_deflev);
|
|
|
|
break;
|
2018-04-17 20:40:38 +03:00
|
|
|
#endif
|
2003-08-14 00:06:49 +04:00
|
|
|
default:
|
2013-12-24 19:48:53 +04:00
|
|
|
panic("%s: unknown af %u", __func__,
|
|
|
|
isr->sp->spidx.src.sa.sa_family);
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#undef IPSEC_CHECK_DEFAULT
|
|
|
|
|
|
|
|
/* set level */
|
|
|
|
switch (isr->level) {
|
|
|
|
case IPSEC_LEVEL_DEFAULT:
|
|
|
|
switch (isr->saidx.proto) {
|
|
|
|
case IPPROTO_ESP:
|
|
|
|
if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
|
|
|
|
level = esp_net_deflev;
|
|
|
|
else
|
|
|
|
level = esp_trans_deflev;
|
|
|
|
break;
|
|
|
|
case IPPROTO_AH:
|
|
|
|
if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
|
|
|
|
level = ah_net_deflev;
|
|
|
|
else
|
|
|
|
level = ah_trans_deflev;
|
2004-10-28 03:10:13 +04:00
|
|
|
break;
|
2003-08-14 00:06:49 +04:00
|
|
|
case IPPROTO_IPCOMP:
|
|
|
|
/*
|
|
|
|
* we don't really care, as IPcomp document says that
|
|
|
|
* we shouldn't compress small packets
|
|
|
|
*/
|
|
|
|
level = IPSEC_LEVEL_USE;
|
|
|
|
break;
|
|
|
|
default:
|
2013-12-24 19:48:53 +04:00
|
|
|
panic("%s: Illegal protocol defined %u", __func__,
|
|
|
|
isr->saidx.proto);
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPSEC_LEVEL_USE:
|
|
|
|
case IPSEC_LEVEL_REQUIRE:
|
|
|
|
level = isr->level;
|
|
|
|
break;
|
|
|
|
case IPSEC_LEVEL_UNIQUE:
|
|
|
|
level = IPSEC_LEVEL_REQUIRE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-12-24 19:48:53 +04:00
|
|
|
panic("%s: Illegal IPsec level %u", __func__, isr->level);
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return level;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-02-16 18:18:41 +03:00
|
|
|
* Check security policy requirements against the actual packet contents.
|
2003-08-14 00:06:49 +04:00
|
|
|
*
|
2018-02-16 18:18:41 +03:00
|
|
|
* If the SP requires an IPsec packet, and the packet was neither AH nor ESP,
|
|
|
|
* then kick it.
|
2003-08-14 00:06:49 +04:00
|
|
|
*/
|
2018-02-28 14:29:14 +03:00
|
|
|
static int
|
2018-02-21 19:18:52 +03:00
|
|
|
ipsec_sp_reject(const struct secpolicy *sp, const struct mbuf *m)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
struct ipsecrequest *isr;
|
|
|
|
|
2017-04-19 06:42:11 +03:00
|
|
|
if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DATA)) {
|
|
|
|
printf("%s: using SP\n", __func__);
|
|
|
|
kdebug_secpolicy(sp);
|
|
|
|
}
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* check policy */
|
|
|
|
switch (sp->policy) {
|
|
|
|
case IPSEC_POLICY_DISCARD:
|
|
|
|
return 1;
|
|
|
|
case IPSEC_POLICY_BYPASS:
|
|
|
|
case IPSEC_POLICY_NONE:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERTMSG(sp->policy == IPSEC_POLICY_IPSEC,
|
|
|
|
"invalid policy %u", sp->policy);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* XXX should compare policy against ipsec header history */
|
|
|
|
|
|
|
|
for (isr = sp->req; isr != NULL; isr = isr->next) {
|
|
|
|
if (ipsec_get_reqlevel(isr) != IPSEC_LEVEL_REQUIRE)
|
|
|
|
continue;
|
|
|
|
switch (isr->saidx.proto) {
|
|
|
|
case IPPROTO_ESP:
|
|
|
|
if ((m->m_flags & M_DECRYPTED) == 0) {
|
2017-04-19 06:42:11 +03:00
|
|
|
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DUMP,
|
|
|
|
"ESP m_flags:%x\n", m->m_flags);
|
2003-08-14 00:06:49 +04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IPPROTO_AH:
|
|
|
|
if ((m->m_flags & M_AUTHIPHDR) == 0) {
|
2017-04-19 06:42:11 +03:00
|
|
|
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DUMP,
|
|
|
|
"AH m_flags:%x\n", m->m_flags);
|
2003-08-14 00:06:49 +04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IPPROTO_IPCOMP:
|
|
|
|
/*
|
2018-02-16 18:18:41 +03:00
|
|
|
* We don't really care, as IPcomp document
|
2003-08-14 00:06:49 +04:00
|
|
|
* says that we shouldn't compress small
|
|
|
|
* packets, IPComp policy should always be
|
|
|
|
* treated as being in "use" level.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-02-21 19:18:52 +03:00
|
|
|
|
|
|
|
return 0;
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-02-26 12:04:29 +03:00
|
|
|
* Check security policy requirements.
|
2003-08-14 00:06:49 +04:00
|
|
|
*/
|
|
|
|
int
|
2018-02-26 12:04:29 +03:00
|
|
|
ipsec_in_reject(struct mbuf *m, void *inp)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
2018-02-21 19:38:15 +03:00
|
|
|
struct inpcb_hdr *inph = (struct inpcb_hdr *)inp;
|
2003-08-14 00:06:49 +04:00
|
|
|
struct secpolicy *sp;
|
|
|
|
int error;
|
|
|
|
int result;
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(m != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2018-02-21 19:38:15 +03:00
|
|
|
if (inph == NULL)
|
2018-02-16 18:18:41 +03:00
|
|
|
sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND,
|
|
|
|
IP_FORWARDING, &error);
|
2003-08-14 00:06:49 +04:00
|
|
|
else
|
2004-01-21 01:55:14 +03:00
|
|
|
sp = ipsec_getpolicybysock(m, IPSEC_DIR_INBOUND,
|
2018-02-21 19:38:15 +03:00
|
|
|
inph, &error);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
if (sp != NULL) {
|
2018-02-21 19:18:52 +03:00
|
|
|
result = ipsec_sp_reject(sp, m);
|
2003-08-14 00:06:49 +04:00
|
|
|
if (result)
|
2008-04-23 10:09:04 +04:00
|
|
|
IPSEC_STATINC(IPSEC_STAT_IN_POLVIO);
|
2017-08-02 04:28:02 +03:00
|
|
|
KEY_SP_UNREF(&sp);
|
2003-08-14 00:06:49 +04:00
|
|
|
} else {
|
2018-02-16 18:18:41 +03:00
|
|
|
result = 0;
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-02-21 19:18:52 +03:00
|
|
|
* Compute the byte size to be occupied by the IPsec header. If it is
|
|
|
|
* tunneled, it includes the size of outer IP header.
|
2003-08-14 00:06:49 +04:00
|
|
|
*/
|
|
|
|
static size_t
|
2018-02-21 19:18:52 +03:00
|
|
|
ipsec_sp_hdrsiz(const struct secpolicy *sp, const struct mbuf *m)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
2017-07-21 05:51:12 +03:00
|
|
|
struct ipsecrequest *isr;
|
2003-08-14 00:06:49 +04:00
|
|
|
size_t siz;
|
|
|
|
|
2017-04-19 06:42:11 +03:00
|
|
|
if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DATA)) {
|
|
|
|
printf("%s: using SP\n", __func__);
|
|
|
|
kdebug_secpolicy(sp);
|
|
|
|
}
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
switch (sp->policy) {
|
|
|
|
case IPSEC_POLICY_DISCARD:
|
|
|
|
case IPSEC_POLICY_BYPASS:
|
|
|
|
case IPSEC_POLICY_NONE:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERTMSG(sp->policy == IPSEC_POLICY_IPSEC,
|
|
|
|
"invalid policy %u", sp->policy);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
siz = 0;
|
|
|
|
for (isr = sp->req; isr != NULL; isr = isr->next) {
|
|
|
|
size_t clen = 0;
|
2017-10-03 11:25:21 +03:00
|
|
|
struct secasvar *sav;
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
switch (isr->saidx.proto) {
|
|
|
|
case IPPROTO_ESP:
|
2017-10-03 11:25:21 +03:00
|
|
|
sav = ipsec_lookup_sa(isr, m);
|
|
|
|
if (sav != NULL) {
|
2017-07-21 05:51:12 +03:00
|
|
|
clen = esp_hdrsiz(sav);
|
2017-08-03 09:32:51 +03:00
|
|
|
KEY_SA_UNREF(&sav);
|
2017-07-21 05:51:12 +03:00
|
|
|
} else
|
|
|
|
clen = esp_hdrsiz(NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
break;
|
|
|
|
case IPPROTO_AH:
|
2017-10-03 11:25:21 +03:00
|
|
|
sav = ipsec_lookup_sa(isr, m);
|
|
|
|
if (sav != NULL) {
|
2017-07-21 05:51:12 +03:00
|
|
|
clen = ah_hdrsiz(sav);
|
2017-08-03 09:32:51 +03:00
|
|
|
KEY_SA_UNREF(&sav);
|
2017-07-21 05:51:12 +03:00
|
|
|
} else
|
|
|
|
clen = ah_hdrsiz(NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
break;
|
|
|
|
case IPPROTO_IPCOMP:
|
|
|
|
clen = sizeof(struct ipcomp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
|
|
|
|
switch (isr->saidx.dst.sa.sa_family) {
|
|
|
|
case AF_INET:
|
|
|
|
clen += sizeof(struct ip);
|
|
|
|
break;
|
|
|
|
#ifdef INET6
|
|
|
|
case AF_INET6:
|
|
|
|
clen += sizeof(struct ip6_hdr);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
2017-05-19 07:34:09 +03:00
|
|
|
IPSECLOG(LOG_ERR, "unknown AF %d in "
|
|
|
|
"IPsec tunnel SA\n",
|
2013-12-24 19:48:53 +04:00
|
|
|
((const struct sockaddr *)&isr->saidx.dst)
|
2017-05-19 07:34:09 +03:00
|
|
|
->sa_family);
|
2003-08-14 00:06:49 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
siz += clen;
|
|
|
|
}
|
|
|
|
|
|
|
|
return siz;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
2018-02-26 11:50:25 +03:00
|
|
|
ipsec_hdrsiz(struct mbuf *m, u_int dir, void *inp)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
2018-02-21 19:38:15 +03:00
|
|
|
struct inpcb_hdr *inph = (struct inpcb_hdr *)inp;
|
2003-08-14 00:06:49 +04:00
|
|
|
struct secpolicy *sp;
|
|
|
|
int error;
|
|
|
|
size_t size;
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(m != NULL);
|
2018-02-21 19:38:15 +03:00
|
|
|
KASSERTMSG(inph == NULL || inph->inph_socket != NULL,
|
|
|
|
"socket w/o inpcb");
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2018-02-21 19:38:15 +03:00
|
|
|
if (inph == NULL)
|
2003-08-14 00:06:49 +04:00
|
|
|
sp = ipsec_getpolicybyaddr(m, dir, IP_FORWARDING, &error);
|
|
|
|
else
|
2018-02-21 19:38:15 +03:00
|
|
|
sp = ipsec_getpolicybysock(m, dir, inph, &error);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
if (sp != NULL) {
|
2018-02-21 19:18:52 +03:00
|
|
|
size = ipsec_sp_hdrsiz(sp, m);
|
2018-02-16 18:18:41 +03:00
|
|
|
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DATA, "size:%zu.\n", size);
|
2017-08-02 04:28:02 +03:00
|
|
|
KEY_SP_UNREF(&sp);
|
2003-08-14 00:06:49 +04:00
|
|
|
} else {
|
2018-02-21 19:38:15 +03:00
|
|
|
size = 0;
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|
2018-02-16 18:18:41 +03:00
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the variable replay window.
|
|
|
|
* ipsec_chkreplay() performs replay check before ICV verification.
|
|
|
|
* ipsec_updatereplay() updates replay bitmap. This must be called after
|
|
|
|
* ICV verification (it also performs replay check, which is usually done
|
|
|
|
* beforehand).
|
|
|
|
* 0 (zero) is returned if packet disallowed, 1 if packet permitted.
|
|
|
|
*
|
|
|
|
* based on RFC 2401.
|
|
|
|
*/
|
|
|
|
int
|
2011-02-18 22:06:45 +03:00
|
|
|
ipsec_chkreplay(u_int32_t seq, const struct secasvar *sav)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
const struct secreplay *replay;
|
|
|
|
u_int32_t diff;
|
|
|
|
int fr;
|
|
|
|
u_int32_t wsizeb; /* constant: bits of window size */
|
|
|
|
int frlast; /* constant: last frame */
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(sav != NULL);
|
|
|
|
KASSERT(sav->replay != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
replay = sav->replay;
|
|
|
|
|
|
|
|
if (replay->wsize == 0)
|
|
|
|
return 1; /* no need to check replay. */
|
|
|
|
|
|
|
|
/* constant */
|
|
|
|
frlast = replay->wsize - 1;
|
|
|
|
wsizeb = replay->wsize << 3;
|
|
|
|
|
|
|
|
/* sequence number of 0 is invalid */
|
|
|
|
if (seq == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* first time is always okay */
|
|
|
|
if (replay->count == 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (seq > replay->lastseq) {
|
|
|
|
/* larger sequences are okay */
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
/* seq is equal or less than lastseq. */
|
|
|
|
diff = replay->lastseq - seq;
|
|
|
|
|
|
|
|
/* over range to check, i.e. too old or wrapped */
|
|
|
|
if (diff >= wsizeb)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fr = frlast - diff / 8;
|
|
|
|
|
|
|
|
/* this packet already seen ? */
|
|
|
|
if ((replay->bitmap)[fr] & (1 << (diff % 8)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* out of order but good */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check replay counter whether to update or not.
|
|
|
|
* OUT: 0: OK
|
|
|
|
* 1: NG
|
|
|
|
*/
|
|
|
|
int
|
2011-02-18 22:06:45 +03:00
|
|
|
ipsec_updatereplay(u_int32_t seq, const struct secasvar *sav)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
struct secreplay *replay;
|
|
|
|
u_int32_t diff;
|
|
|
|
int fr;
|
|
|
|
u_int32_t wsizeb; /* constant: bits of window size */
|
|
|
|
int frlast; /* constant: last frame */
|
|
|
|
|
2017-04-18 08:26:41 +03:00
|
|
|
KASSERT(sav != NULL);
|
|
|
|
KASSERT(sav->replay != NULL);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
replay = sav->replay;
|
|
|
|
|
|
|
|
if (replay->wsize == 0)
|
|
|
|
goto ok; /* no need to check replay. */
|
|
|
|
|
|
|
|
/* constant */
|
|
|
|
frlast = replay->wsize - 1;
|
|
|
|
wsizeb = replay->wsize << 3;
|
|
|
|
|
|
|
|
/* sequence number of 0 is invalid */
|
|
|
|
if (seq == 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* first time */
|
|
|
|
if (replay->count == 0) {
|
|
|
|
replay->lastseq = seq;
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(replay->bitmap, 0, replay->wsize);
|
2003-08-14 00:06:49 +04:00
|
|
|
(replay->bitmap)[frlast] = 1;
|
|
|
|
goto ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (seq > replay->lastseq) {
|
|
|
|
/* seq is larger than lastseq. */
|
|
|
|
diff = seq - replay->lastseq;
|
|
|
|
|
|
|
|
/* new larger sequence number */
|
|
|
|
if (diff < wsizeb) {
|
|
|
|
/* In window */
|
|
|
|
/* set bit for this packet */
|
|
|
|
vshiftl(replay->bitmap, diff, replay->wsize);
|
|
|
|
(replay->bitmap)[frlast] |= 1;
|
|
|
|
} else {
|
|
|
|
/* this packet has a "way larger" */
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(replay->bitmap, 0, replay->wsize);
|
2003-08-14 00:06:49 +04:00
|
|
|
(replay->bitmap)[frlast] = 1;
|
|
|
|
}
|
|
|
|
replay->lastseq = seq;
|
|
|
|
|
|
|
|
/* larger is good */
|
|
|
|
} else {
|
|
|
|
/* seq is equal or less than lastseq. */
|
|
|
|
diff = replay->lastseq - seq;
|
|
|
|
|
|
|
|
/* over range to check, i.e. too old or wrapped */
|
|
|
|
if (diff >= wsizeb)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
fr = frlast - diff / 8;
|
|
|
|
|
|
|
|
/* this packet already seen ? */
|
|
|
|
if ((replay->bitmap)[fr] & (1 << (diff % 8)))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* mark as seen */
|
|
|
|
(replay->bitmap)[fr] |= (1 << (diff % 8));
|
|
|
|
|
|
|
|
/* out of order but good */
|
|
|
|
}
|
|
|
|
|
|
|
|
ok:
|
|
|
|
if (replay->count == ~0) {
|
2017-10-17 10:23:08 +03:00
|
|
|
char buf[IPSEC_LOGSASTRLEN];
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/* set overflow flag */
|
|
|
|
replay->overflow++;
|
|
|
|
|
|
|
|
/* don't increment, no more packets accepted */
|
|
|
|
if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0)
|
|
|
|
return 1;
|
|
|
|
|
2017-05-19 07:34:09 +03:00
|
|
|
IPSECLOG(LOG_WARNING, "replay counter made %d cycle. %s\n",
|
|
|
|
replay->overflow, ipsec_logsastr(sav, buf, sizeof(buf)));
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
replay->count++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-02-21 19:18:52 +03:00
|
|
|
* shift variable length buffer to left.
|
2003-08-14 00:06:49 +04:00
|
|
|
* IN: bitmap: pointer to the buffer
|
2018-02-16 14:07:44 +03:00
|
|
|
* nbit: the number of to shift.
|
2003-08-14 00:06:49 +04:00
|
|
|
* wsize: buffer size (bytes).
|
|
|
|
*/
|
|
|
|
static void
|
2007-07-07 22:38:22 +04:00
|
|
|
vshiftl(unsigned char *bitmap, int nbit, int wsize)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
int s, j, i;
|
|
|
|
unsigned char over;
|
|
|
|
|
|
|
|
for (j = 0; j < nbit; j += 8) {
|
|
|
|
s = (nbit - j < 8) ? (nbit - j): 8;
|
|
|
|
bitmap[0] <<= s;
|
|
|
|
for (i = 1; i < wsize; i++) {
|
|
|
|
over = (bitmap[i] >> (8 - s));
|
|
|
|
bitmap[i] <<= s;
|
|
|
|
bitmap[i-1] |= over;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return a printable string for the address. */
|
2005-06-10 17:22:42 +04:00
|
|
|
const char *
|
2017-05-11 08:55:14 +03:00
|
|
|
ipsec_address(const union sockaddr_union *sa, char *buf, size_t size)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
switch (sa->sa.sa_family) {
|
|
|
|
case AF_INET:
|
2017-05-11 08:55:14 +03:00
|
|
|
in_print(buf, size, &sa->sin.sin_addr);
|
|
|
|
return buf;
|
2003-08-14 00:06:49 +04:00
|
|
|
#if INET6
|
|
|
|
case AF_INET6:
|
2017-05-11 08:55:14 +03:00
|
|
|
in6_print(buf, size, &sa->sin6.sin6_addr);
|
|
|
|
return buf;
|
2018-02-16 14:07:44 +03:00
|
|
|
#endif
|
2003-08-14 00:06:49 +04:00
|
|
|
default:
|
|
|
|
return "(unknown address family)";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
2017-05-11 08:55:14 +03:00
|
|
|
ipsec_logsastr(const struct secasvar *sav, char *buf, size_t size)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
2011-02-18 22:06:45 +03:00
|
|
|
const struct secasindex *saidx = &sav->sah->saidx;
|
2017-05-11 08:55:14 +03:00
|
|
|
char sbuf[IPSEC_ADDRSTRLEN], dbuf[IPSEC_ADDRSTRLEN];
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2017-04-19 06:28:19 +03:00
|
|
|
KASSERTMSG(saidx->src.sa.sa_family == saidx->dst.sa.sa_family,
|
|
|
|
"af family mismatch, src %u, dst %u",
|
|
|
|
saidx->src.sa.sa_family, saidx->dst.sa.sa_family);
|
2003-08-14 00:06:49 +04:00
|
|
|
|
2017-05-11 08:55:14 +03:00
|
|
|
snprintf(buf, size, "SA(SPI=%u src=%s dst=%s)",
|
|
|
|
(u_int32_t)ntohl(sav->spi),
|
|
|
|
ipsec_address(&saidx->src, sbuf, sizeof(sbuf)),
|
|
|
|
ipsec_address(&saidx->dst, dbuf, sizeof(dbuf)));
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2007-02-10 12:43:05 +03:00
|
|
|
#ifdef INET6
|
2018-02-16 14:07:44 +03:00
|
|
|
struct secpolicy *
|
|
|
|
ipsec6_check_policy(struct mbuf *m, struct in6pcb *in6p, int flags,
|
|
|
|
int *needipsecp, int *errorp)
|
2007-02-10 12:43:05 +03:00
|
|
|
{
|
|
|
|
struct secpolicy *sp = NULL;
|
|
|
|
int s;
|
|
|
|
int error = 0;
|
|
|
|
int needipsec = 0;
|
|
|
|
|
2018-03-03 12:54:55 +03:00
|
|
|
if (ipsec_outdone(m)) {
|
|
|
|
goto skippolicycheck;
|
|
|
|
}
|
|
|
|
s = splsoftnet();
|
|
|
|
if (in6p && ipsec_pcb_skip_ipsec(in6p->in6p_sp, IPSEC_DIR_OUTBOUND)) {
|
2007-12-29 17:53:24 +03:00
|
|
|
splx(s);
|
2018-03-03 12:54:55 +03:00
|
|
|
goto skippolicycheck;
|
|
|
|
}
|
|
|
|
sp = ipsec_checkpolicy(m, IPSEC_DIR_OUTBOUND, flags, &error, in6p);
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There are four return cases:
|
|
|
|
* sp != NULL apply IPsec policy
|
|
|
|
* sp == NULL, error == 0 no IPsec handling needed
|
|
|
|
* sp == NULL, error == -EINVAL discard packet w/o error
|
|
|
|
* sp == NULL, error != 0 discard packet, report error
|
|
|
|
*/
|
|
|
|
if (sp == NULL) {
|
|
|
|
needipsec = 0;
|
|
|
|
} else {
|
|
|
|
needipsec = 1;
|
2007-02-10 12:43:05 +03:00
|
|
|
}
|
|
|
|
|
2018-03-03 12:54:55 +03:00
|
|
|
skippolicycheck:
|
2007-02-10 12:43:05 +03:00
|
|
|
*errorp = error;
|
|
|
|
*needipsecp = needipsec;
|
|
|
|
return sp;
|
|
|
|
}
|
2018-11-22 07:48:34 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* calculate UDP checksum for UDP encapsulated ESP for IPv6.
|
|
|
|
*
|
|
|
|
* RFC2460(Internet Protocol, Version 6 Specification) says:
|
|
|
|
*
|
|
|
|
* IPv6 receivers MUST discard UDP packets with a zero checksum.
|
|
|
|
*
|
|
|
|
* There is more relaxed speficication RFC6935(IPv6 and UDP Checksums for
|
|
|
|
* Tunneled Packets). The document allows zero checksum. It's too
|
|
|
|
* late to publish, there are a lot of interoperability problems...
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ipsec6_udp_cksum(struct mbuf *m)
|
|
|
|
{
|
|
|
|
struct ip6_hdr *ip6;
|
|
|
|
uint16_t plen, uh_sum;
|
|
|
|
int off;
|
|
|
|
|
|
|
|
/* must called after m_pullup() */
|
|
|
|
KASSERT(m->m_len >= sizeof(struct ip6_hdr));
|
|
|
|
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
KASSERT(ip6->ip6_nxt == IPPROTO_UDP);
|
|
|
|
|
|
|
|
/* ip6->ip6_plen can not be updated before ip6_output() */
|
|
|
|
plen = m->m_pkthdr.len - sizeof(*ip6);
|
|
|
|
KASSERT(plen >= sizeof(struct udphdr));
|
|
|
|
|
|
|
|
uh_sum = in6_cksum(m, IPPROTO_UDP, sizeof(*ip6), plen);
|
|
|
|
if (uh_sum == 0)
|
|
|
|
uh_sum = 0xffff;
|
|
|
|
|
|
|
|
off = sizeof(*ip6) + offsetof(struct udphdr, uh_sum);
|
|
|
|
m_copyback(m, off, sizeof(uh_sum), (void *)&uh_sum);
|
|
|
|
}
|
2015-04-01 05:49:44 +03:00
|
|
|
#endif /* INET6 */
|
2007-02-10 12:43:05 +03:00
|
|
|
|
2018-02-26 09:17:01 +03:00
|
|
|
/*
|
|
|
|
* -----------------------------------------------------------------------------
|
|
|
|
*/
|
2007-02-10 12:43:05 +03:00
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
/* XXX this stuff doesn't belong here... */
|
|
|
|
|
2018-02-26 09:17:01 +03:00
|
|
|
static struct xformsw *xforms = NULL;
|
2003-08-14 00:06:49 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Register a transform; typically at system startup.
|
|
|
|
*/
|
|
|
|
void
|
2011-05-16 14:00:32 +04:00
|
|
|
xform_register(struct xformsw *xsp)
|
2003-08-14 00:06:49 +04:00
|
|
|
{
|
|
|
|
xsp->xf_next = xforms;
|
|
|
|
xforms = xsp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize transform support in an sav.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
xform_init(struct secasvar *sav, int xftype)
|
|
|
|
{
|
|
|
|
struct xformsw *xsp;
|
|
|
|
|
|
|
|
if (sav->tdb_xform != NULL) /* previously initialized */
|
|
|
|
return 0;
|
|
|
|
for (xsp = xforms; xsp; xsp = xsp->xf_next)
|
|
|
|
if (xsp->xf_type == xftype)
|
|
|
|
return (*xsp->xf_init)(sav, xsp);
|
|
|
|
|
2017-05-19 07:34:09 +03:00
|
|
|
IPSECLOG(LOG_DEBUG, "no match for xform type %d\n", xftype);
|
2003-08-14 00:06:49 +04:00
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2008-04-23 10:09:04 +04:00
|
|
|
/*
|
|
|
|
* XXXJRT This should be done as a protosw init call.
|
|
|
|
*/
|
2003-08-14 00:06:49 +04:00
|
|
|
void
|
|
|
|
ipsec_attach(void)
|
|
|
|
{
|
2008-04-23 10:09:04 +04:00
|
|
|
|
2017-08-10 09:11:24 +03:00
|
|
|
ipsec_output_init();
|
|
|
|
|
2008-04-23 10:09:04 +04:00
|
|
|
ipsecstat_percpu = percpu_alloc(sizeof(uint64_t) * IPSEC_NSTATS);
|
|
|
|
|
2017-04-06 12:20:07 +03:00
|
|
|
sysctl_net_inet_ipsec_setup(NULL);
|
|
|
|
#ifdef INET6
|
|
|
|
sysctl_net_inet6_ipsec6_setup(NULL);
|
|
|
|
#endif
|
|
|
|
|
2003-08-14 00:06:49 +04:00
|
|
|
ah_attach();
|
|
|
|
esp_attach();
|
|
|
|
ipcomp_attach();
|
|
|
|
ipe4_attach();
|
Initial commit of a port of the FreeBSD implementation of RFC 2385
(MD5 signatures for TCP, as used with BGP). Credit for original
FreeBSD code goes to Bruce M. Simpson, with FreeBSD sponsorship
credited to sentex.net. Shortening of the setsockopt() name
attributed to Vincent Jardin.
This commit is a minimal, working version of the FreeBSD code, as
MFC'ed to FreeBSD-4. It has received minimal testing with a ttcp
modified to set the TCP-MD5 option; BMS's additions to tcpdump-current
(tcpdump -M) confirm that the MD5 signatures are correct. Committed
as-is for further testing between a NetBSD BGP speaker (e.g., quagga)
and industry-standard BGP speakers (e.g., Cisco, Juniper).
NOTE: This version has two potential flaws. First, I do see any code
that verifies recieved TCP-MD5 signatures. Second, the TCP-MD5
options are internally padded and assumed to be 32-bit aligned. A more
space-efficient scheme is to pack all TCP options densely (and
possibly unaligned) into the TCP header ; then do one final padding to
a 4-byte boundary. Pre-existing comments note that accounting for
TCP-option space when we add SACK is yet to be done. For now, I'm
punting on that; we can solve it properly, in a way that will handle
SACK blocks, as a separate exercise.
In case a pullup to NetBSD-2 is requested, this adds sys/netipsec/xform_tcp.c
,and modifies:
sys/net/pfkeyv2.h,v 1.15
sys/netinet/files.netinet,v 1.5
sys/netinet/ip.h,v 1.25
sys/netinet/tcp.h,v 1.15
sys/netinet/tcp_input.c,v 1.200
sys/netinet/tcp_output.c,v 1.109
sys/netinet/tcp_subr.c,v 1.165
sys/netinet/tcp_usrreq.c,v 1.89
sys/netinet/tcp_var.h,v 1.109
sys/netipsec/files.netipsec,v 1.3
sys/netipsec/ipsec.c,v 1.11
sys/netipsec/ipsec.h,v 1.7
sys/netipsec/key.c,v 1.11
share/man/man4/tcp.4,v 1.16
lib/libipsec/pfkey.c,v 1.20
lib/libipsec/pfkey_dump.c,v 1.17
lib/libipsec/policy_token.l,v 1.8
sbin/setkey/parse.y,v 1.14
sbin/setkey/setkey.8,v 1.27
sbin/setkey/token.l,v 1.15
Note that the preceding two revisions to tcp.4 will be
required to cleanly apply this diff.
2004-04-26 02:25:03 +04:00
|
|
|
#ifdef TCP_SIGNATURE
|
|
|
|
tcpsignature_attach();
|
|
|
|
#endif
|
2003-08-14 00:06:49 +04:00
|
|
|
}
|