2003-03-28 20:28:05 +00:00
|
|
|
/* $OpenBSD: ipsec_input.c,v 1.63 2003/02/20 18:35:43 deraadt Exp $ */
|
2005-01-07 01:45:51 +00:00
|
|
|
/*-
|
2003-03-28 20:28:05 +00:00
|
|
|
* The authors of this code are John Ioannidis (ji@tla.org),
|
|
|
|
* Angelos D. Keromytis (kermit@csd.uch.gr) and
|
|
|
|
* Niels Provos (provos@physnet.uni-hamburg.de).
|
|
|
|
*
|
|
|
|
* This code was written by John Ioannidis for BSD/OS in Athens, Greece,
|
|
|
|
* in November 1995.
|
|
|
|
*
|
|
|
|
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
|
|
|
|
* by Angelos D. Keromytis.
|
|
|
|
*
|
|
|
|
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
|
|
|
|
* and Niels Provos.
|
|
|
|
*
|
|
|
|
* Additional features in 1999 by Angelos D. Keromytis.
|
|
|
|
*
|
|
|
|
* Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
|
|
|
|
* Angelos D. Keromytis and Niels Provos.
|
|
|
|
* Copyright (c) 2001, Angelos D. Keromytis.
|
2017-02-06 08:49:57 +00:00
|
|
|
* Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
|
2003-03-28 20:28:05 +00:00
|
|
|
*
|
|
|
|
* Permission to use, copy, and modify this software with or without fee
|
|
|
|
* is hereby granted, provided that this entire notice is included in
|
|
|
|
* all copies of any software which is or includes a copy or
|
|
|
|
* modification of this software.
|
|
|
|
* You may use this code under the GNU public license if you so wish. Please
|
|
|
|
* contribute changes back to the authors under this freer than GPL license
|
|
|
|
* so that we may further the use of strong encryption without limitations to
|
|
|
|
* all.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
|
|
|
|
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
|
|
|
|
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
|
|
|
|
* PURPOSE.
|
|
|
|
*/
|
2002-10-16 02:10:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* IPsec input processing.
|
|
|
|
*/
|
|
|
|
|
2017-02-06 08:49:57 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
2002-10-16 02:10:08 +00:00
|
|
|
#include "opt_inet.h"
|
|
|
|
#include "opt_inet6.h"
|
|
|
|
#include "opt_ipsec.h"
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/domain.h>
|
|
|
|
#include <sys/protosw.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/errno.h>
|
2015-11-25 07:31:59 +00:00
|
|
|
#include <sys/hhook.h>
|
2002-10-16 02:10:08 +00:00
|
|
|
#include <sys/syslog.h>
|
|
|
|
|
|
|
|
#include <net/if.h>
|
2013-10-26 17:58:36 +00:00
|
|
|
#include <net/if_var.h>
|
2015-11-25 07:31:59 +00:00
|
|
|
#include <net/if_enc.h>
|
2002-10-16 02:10:08 +00:00
|
|
|
#include <net/netisr.h>
|
Build on Jeff Roberson's linker-set based dynamic per-CPU allocator
(DPCPU), as suggested by Peter Wemm, and implement a new per-virtual
network stack memory allocator. Modify vnet to use the allocator
instead of monolithic global container structures (vinet, ...). This
change solves many binary compatibility problems associated with
VIMAGE, and restores ELF symbols for virtualized global variables.
Each virtualized global variable exists as a "reference copy", and also
once per virtual network stack. Virtualized global variables are
tagged at compile-time, placing the in a special linker set, which is
loaded into a contiguous region of kernel memory. Virtualized global
variables in the base kernel are linked as normal, but those in modules
are copied and relocated to a reserved portion of the kernel's vnet
region with the help of a the kernel linker.
Virtualized global variables exist in per-vnet memory set up when the
network stack instance is created, and are initialized statically from
the reference copy. Run-time access occurs via an accessor macro, which
converts from the current vnet and requested symbol to a per-vnet
address. When "options VIMAGE" is not compiled into the kernel, normal
global ELF symbols will be used instead and indirection is avoided.
This change restores static initialization for network stack global
variables, restores support for non-global symbols and types, eliminates
the need for many subsystem constructors, eliminates large per-subsystem
structures that caused many binary compatibility issues both for
monitoring applications (netstat) and kernel modules, removes the
per-function INIT_VNET_*() macros throughout the stack, eliminates the
need for vnet_symmap ksym(2) munging, and eliminates duplicate
definitions of virtualized globals under VIMAGE_GLOBALS.
Bump __FreeBSD_version and update UPDATING.
Portions submitted by: bz
Reviewed by: bz, zec
Discussed with: gnn, jamie, jeff, jhb, julian, sam
Suggested by: peter
Approved by: re (kensmith)
2009-07-14 22:48:30 +00:00
|
|
|
#include <net/vnet.h>
|
2002-10-16 02:10:08 +00:00
|
|
|
|
|
|
|
#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/ip6.h>
|
|
|
|
#ifdef INET6
|
|
|
|
#include <netinet6/ip6_var.h>
|
|
|
|
#endif
|
|
|
|
#include <netinet/in_pcb.h>
|
|
|
|
#ifdef INET6
|
|
|
|
#include <netinet/icmp6.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <netipsec/ipsec.h>
|
|
|
|
#ifdef INET6
|
|
|
|
#include <netipsec/ipsec6.h>
|
|
|
|
#endif
|
|
|
|
#include <netipsec/ah_var.h>
|
|
|
|
#include <netipsec/esp.h>
|
|
|
|
#include <netipsec/esp_var.h>
|
|
|
|
#include <netipsec/ipcomp_var.h>
|
|
|
|
|
|
|
|
#include <netipsec/key.h>
|
|
|
|
#include <netipsec/keydb.h>
|
2017-02-06 08:49:57 +00:00
|
|
|
#include <netipsec/key_debug.h>
|
2002-10-16 02:10:08 +00:00
|
|
|
|
|
|
|
#include <netipsec/xform.h>
|
|
|
|
#include <netinet6/ip6protosw.h>
|
|
|
|
|
|
|
|
#include <machine/in_cksum.h>
|
|
|
|
#include <machine/stdarg.h>
|
|
|
|
|
2008-08-12 09:05:01 +00:00
|
|
|
|
2013-06-20 11:44:16 +00:00
|
|
|
#define IPSEC_ISTAT(proto, name) do { \
|
|
|
|
if ((proto) == IPPROTO_ESP) \
|
|
|
|
ESPSTAT_INC(esps_##name); \
|
|
|
|
else if ((proto) == IPPROTO_AH) \
|
|
|
|
AHSTAT_INC(ahs_##name); \
|
|
|
|
else \
|
|
|
|
IPCOMPSTAT_INC(ipcomps_##name); \
|
|
|
|
} while (0)
|
2002-10-16 02:10:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ipsec_common_input gets called when an IPsec-protected packet
|
2010-10-25 16:11:37 +00:00
|
|
|
* is received by IPv4 or IPv6. Its job is to find the right SA
|
2007-06-15 21:34:12 +00:00
|
|
|
* and call the appropriate transform. The transform callback
|
2002-10-16 02:10:08 +00:00
|
|
|
* takes care of further processing (like ingress filtering).
|
|
|
|
*/
|
2017-02-06 08:49:57 +00:00
|
|
|
static int
|
2002-10-16 02:10:08 +00:00
|
|
|
ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
|
|
|
|
{
|
2017-05-29 09:30:38 +00:00
|
|
|
IPSEC_DEBUG_DECLARE(char buf[IPSEC_ADDRSTRLEN]);
|
2002-10-16 02:10:08 +00:00
|
|
|
union sockaddr_union dst_address;
|
|
|
|
struct secasvar *sav;
|
2017-02-06 08:49:57 +00:00
|
|
|
uint32_t spi;
|
2003-09-01 05:35:55 +00:00
|
|
|
int error;
|
2002-10-16 02:10:08 +00:00
|
|
|
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, input);
|
2002-10-16 02:10:08 +00:00
|
|
|
|
2003-09-29 22:57:43 +00:00
|
|
|
IPSEC_ASSERT(m != NULL, ("null packet"));
|
2002-10-16 02:10:08 +00:00
|
|
|
|
2007-06-15 21:32:51 +00:00
|
|
|
IPSEC_ASSERT(sproto == IPPROTO_ESP || sproto == IPPROTO_AH ||
|
|
|
|
sproto == IPPROTO_IPCOMP,
|
|
|
|
("unexpected security protocol %u", sproto));
|
|
|
|
|
Commit step 1 of the vimage project, (network stack)
virtualization work done by Marko Zec (zec@).
This is the first in a series of commits over the course
of the next few weeks.
Mark all uses of global variables to be virtualized
with a V_ prefix.
Use macros to map them back to their global names for
now, so this is a NOP change only.
We hope to have caught at least 85-90% of what is needed
so we do not invalidate a lot of outstanding patches again.
Obtained from: //depot/projects/vimage-commit2/...
Reviewed by: brooks, des, ed, mav, julian,
jamie, kris, rwatson, zec, ...
(various people I forgot, different versions)
md5 (with a bit of help)
Sponsored by: NLnet Foundation, The FreeBSD Foundation
X-MFC after: never
V_Commit_Message_Reviewed_By: more people than the patch
2008-08-17 23:27:27 +00:00
|
|
|
if ((sproto == IPPROTO_ESP && !V_esp_enable) ||
|
|
|
|
(sproto == IPPROTO_AH && !V_ah_enable) ||
|
|
|
|
(sproto == IPPROTO_IPCOMP && !V_ipcomp_enable)) {
|
2002-10-16 02:10:08 +00:00
|
|
|
m_freem(m);
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, pdrops);
|
2002-10-16 02:10:08 +00:00
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->m_pkthdr.len - skip < 2 * sizeof (u_int32_t)) {
|
|
|
|
m_freem(m);
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, hdrops);
|
2003-09-29 22:57:43 +00:00
|
|
|
DPRINTF(("%s: packet too small\n", __func__));
|
2002-10-16 02:10:08 +00:00
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Retrieve the SPI from the relevant IPsec header */
|
|
|
|
if (sproto == IPPROTO_ESP)
|
|
|
|
m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi);
|
|
|
|
else if (sproto == IPPROTO_AH)
|
|
|
|
m_copydata(m, skip + sizeof(u_int32_t), sizeof(u_int32_t),
|
|
|
|
(caddr_t) &spi);
|
|
|
|
else if (sproto == IPPROTO_IPCOMP) {
|
|
|
|
u_int16_t cpi;
|
|
|
|
m_copydata(m, skip + sizeof(u_int16_t), sizeof(u_int16_t),
|
|
|
|
(caddr_t) &cpi);
|
|
|
|
spi = ntohl(htons(cpi));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the SA and (indirectly) call the appropriate
|
|
|
|
* kernel crypto routine. The resulting mbuf chain is a valid
|
|
|
|
* IP packet ready to go through input processing.
|
|
|
|
*/
|
|
|
|
bzero(&dst_address, sizeof (dst_address));
|
|
|
|
dst_address.sa.sa_family = af;
|
|
|
|
switch (af) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
|
|
|
dst_address.sin.sin_len = sizeof(struct sockaddr_in);
|
|
|
|
m_copydata(m, offsetof(struct ip, ip_dst),
|
|
|
|
sizeof(struct in_addr),
|
|
|
|
(caddr_t) &dst_address.sin.sin_addr);
|
|
|
|
break;
|
|
|
|
#endif /* INET */
|
|
|
|
#ifdef INET6
|
|
|
|
case AF_INET6:
|
|
|
|
dst_address.sin6.sin6_len = sizeof(struct sockaddr_in6);
|
|
|
|
m_copydata(m, offsetof(struct ip6_hdr, ip6_dst),
|
|
|
|
sizeof(struct in6_addr),
|
|
|
|
(caddr_t) &dst_address.sin6.sin6_addr);
|
2015-04-18 16:46:31 +00:00
|
|
|
/* We keep addresses in SADB without embedded scope id */
|
|
|
|
if (IN6_IS_SCOPE_LINKLOCAL(&dst_address.sin6.sin6_addr)) {
|
|
|
|
/* XXX: sa6_recoverscope() */
|
|
|
|
dst_address.sin6.sin6_scope_id =
|
|
|
|
ntohs(dst_address.sin6.sin6_addr.s6_addr16[1]);
|
|
|
|
dst_address.sin6.sin6_addr.s6_addr16[1] = 0;
|
|
|
|
}
|
2002-10-16 02:10:08 +00:00
|
|
|
break;
|
|
|
|
#endif /* INET6 */
|
|
|
|
default:
|
2003-09-29 22:57:43 +00:00
|
|
|
DPRINTF(("%s: unsupported protocol family %u\n", __func__, af));
|
2002-10-16 02:10:08 +00:00
|
|
|
m_freem(m);
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, nopf);
|
2002-10-16 02:10:08 +00:00
|
|
|
return EPFNOSUPPORT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NB: only pass dst since key_allocsa follows RFC2401 */
|
2017-02-06 08:49:57 +00:00
|
|
|
sav = key_allocsa(&dst_address, sproto, spi);
|
2002-10-16 02:10:08 +00:00
|
|
|
if (sav == NULL) {
|
2003-09-29 22:57:43 +00:00
|
|
|
DPRINTF(("%s: no key association found for SA %s/%08lx/%u\n",
|
2015-04-18 16:58:33 +00:00
|
|
|
__func__, ipsec_address(&dst_address, buf, sizeof(buf)),
|
|
|
|
(u_long) ntohl(spi), sproto));
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, notdb);
|
2002-10-16 02:10:08 +00:00
|
|
|
m_freem(m);
|
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sav->tdb_xform == NULL) {
|
2003-09-29 22:57:43 +00:00
|
|
|
DPRINTF(("%s: attempted to use uninitialized SA %s/%08lx/%u\n",
|
2015-04-18 16:58:33 +00:00
|
|
|
__func__, ipsec_address(&dst_address, buf, sizeof(buf)),
|
|
|
|
(u_long) ntohl(spi), sproto));
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, noxform);
|
2017-02-06 08:49:57 +00:00
|
|
|
key_freesav(&sav);
|
2002-10-16 02:10:08 +00:00
|
|
|
m_freem(m);
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Call appropriate transform and return -- callback takes care of
|
|
|
|
* everything else.
|
|
|
|
*/
|
|
|
|
error = (*sav->tdb_xform->xf_input)(m, sav, skip, protoff);
|
2017-02-06 08:49:57 +00:00
|
|
|
return (error);
|
2002-10-16 02:10:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef INET
|
2017-02-06 08:49:57 +00:00
|
|
|
extern struct protosw inetsw[];
|
2002-11-08 23:37:50 +00:00
|
|
|
|
2017-02-06 08:49:57 +00:00
|
|
|
/*
|
|
|
|
* IPSEC_INPUT() method implementation for IPv4.
|
|
|
|
* 0 - Permitted by inbound security policy for further processing.
|
|
|
|
* EACCES - Forbidden by inbound security policy.
|
|
|
|
* EINPROGRESS - consumed by IPsec.
|
|
|
|
*/
|
2014-08-08 01:57:15 +00:00
|
|
|
int
|
2017-02-06 08:49:57 +00:00
|
|
|
ipsec4_input(struct mbuf *m, int offset, int proto)
|
2002-11-08 23:37:50 +00:00
|
|
|
{
|
2014-08-08 01:57:15 +00:00
|
|
|
|
2017-02-06 08:49:57 +00:00
|
|
|
switch (proto) {
|
|
|
|
case IPPROTO_AH:
|
|
|
|
case IPPROTO_ESP:
|
|
|
|
case IPPROTO_IPCOMP:
|
|
|
|
/* Do inbound IPsec processing for AH/ESP/IPCOMP */
|
|
|
|
ipsec_common_input(m, offset,
|
|
|
|
offsetof(struct ip, ip_p), AF_INET, proto);
|
|
|
|
return (EINPROGRESS); /* mbuf consumed by IPsec */
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* Protocols with further headers get their IPsec treatment
|
|
|
|
* within the protocol specific processing.
|
|
|
|
*/
|
|
|
|
if ((inetsw[ip_protox[proto]].pr_flags & PR_LASTHDR) == 0)
|
|
|
|
return (0);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
};
|
|
|
|
/*
|
|
|
|
* Enforce IPsec policy checking if we are seeing last header.
|
|
|
|
*/
|
|
|
|
if (ipsec4_in_reject(m, NULL) != 0) {
|
|
|
|
/* Forbidden by inbound security policy */
|
|
|
|
m_freem(m);
|
|
|
|
return (EACCES);
|
|
|
|
}
|
|
|
|
return (0);
|
2002-11-08 23:37:50 +00:00
|
|
|
}
|
|
|
|
|
2002-10-16 02:10:08 +00:00
|
|
|
/*
|
|
|
|
* IPsec input callback for INET protocols.
|
|
|
|
* This routine is called as the transform callback.
|
|
|
|
* Takes care of filtering and other sanity checks on
|
|
|
|
* the processed packet.
|
|
|
|
*/
|
|
|
|
int
|
2014-12-11 17:14:49 +00:00
|
|
|
ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
|
|
|
|
int protoff)
|
2002-10-16 02:10:08 +00:00
|
|
|
{
|
2017-05-29 09:30:38 +00:00
|
|
|
IPSEC_DEBUG_DECLARE(char buf[IPSEC_ADDRSTRLEN]);
|
2015-11-25 07:31:59 +00:00
|
|
|
struct ipsec_ctx_data ctx;
|
2017-02-06 08:49:57 +00:00
|
|
|
struct xform_history *xh;
|
2002-10-16 02:10:08 +00:00
|
|
|
struct secasindex *saidx;
|
2017-02-06 08:49:57 +00:00
|
|
|
struct m_tag *mtag;
|
|
|
|
struct ip *ip;
|
|
|
|
int error, prot, af, sproto, isr_prot;
|
2002-10-16 02:10:08 +00:00
|
|
|
|
2003-09-29 22:57:43 +00:00
|
|
|
IPSEC_ASSERT(sav != NULL, ("null SA"));
|
|
|
|
IPSEC_ASSERT(sav->sah != NULL, ("null SAH"));
|
2002-10-16 02:10:08 +00:00
|
|
|
saidx = &sav->sah->saidx;
|
|
|
|
af = saidx->dst.sa.sa_family;
|
2003-09-29 22:57:43 +00:00
|
|
|
IPSEC_ASSERT(af == AF_INET, ("unexpected af %u", af));
|
2002-10-16 02:10:08 +00:00
|
|
|
sproto = saidx->proto;
|
2003-09-29 22:57:43 +00:00
|
|
|
IPSEC_ASSERT(sproto == IPPROTO_ESP || sproto == IPPROTO_AH ||
|
2002-10-16 02:10:08 +00:00
|
|
|
sproto == IPPROTO_IPCOMP,
|
2003-09-29 22:57:43 +00:00
|
|
|
("unexpected security protocol %u", sproto));
|
2002-10-16 02:10:08 +00:00
|
|
|
|
|
|
|
if (skip != 0) {
|
2012-10-23 08:22:01 +00:00
|
|
|
/*
|
|
|
|
* Fix IPv4 header
|
|
|
|
*/
|
2002-10-16 02:10:08 +00:00
|
|
|
if (m->m_len < skip && (m = m_pullup(m, skip)) == NULL) {
|
2003-09-29 22:57:43 +00:00
|
|
|
DPRINTF(("%s: processing failed for SA %s/%08lx\n",
|
2015-04-18 16:58:33 +00:00
|
|
|
__func__, ipsec_address(&sav->sah->saidx.dst,
|
|
|
|
buf, sizeof(buf)), (u_long) ntohl(sav->spi)));
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, hdrops);
|
2002-10-16 02:10:08 +00:00
|
|
|
error = ENOBUFS;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
ip->ip_len = htons(m->m_pkthdr.len);
|
|
|
|
ip->ip_sum = 0;
|
|
|
|
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
|
|
|
|
} else {
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
}
|
|
|
|
prot = ip->ip_p;
|
2017-02-06 08:49:57 +00:00
|
|
|
/*
|
|
|
|
* Check that we have NAT-T enabled and apply transport mode
|
|
|
|
* decapsulation NAT procedure (RFC3948).
|
|
|
|
* Do this before invoking into the PFIL.
|
|
|
|
*/
|
|
|
|
if (sav->natt != NULL &&
|
|
|
|
(prot == IPPROTO_UDP || prot == IPPROTO_TCP))
|
|
|
|
udp_ipsec_adjust_cksum(m, sav, prot, skip);
|
2002-10-16 02:10:08 +00:00
|
|
|
|
2017-07-31 11:04:35 +00:00
|
|
|
IPSEC_INIT_CTX(&ctx, &m, NULL, sav, AF_INET, IPSEC_ENC_BEFORE);
|
2015-11-25 07:31:59 +00:00
|
|
|
if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0)
|
|
|
|
goto bad;
|
2017-02-06 08:49:57 +00:00
|
|
|
ip = mtod(m, struct ip *); /* update pointer */
|
2014-05-28 12:45:27 +00:00
|
|
|
|
2002-10-16 02:10:08 +00:00
|
|
|
/* IP-in-IP encapsulation */
|
2014-10-02 02:00:21 +00:00
|
|
|
if (prot == IPPROTO_IPIP &&
|
|
|
|
saidx->mode != IPSEC_MODE_TRANSPORT) {
|
2003-08-13 22:36:24 +00:00
|
|
|
if (m->m_pkthdr.len - skip < sizeof(struct ip)) {
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, hdrops);
|
2003-08-13 22:36:24 +00:00
|
|
|
error = EINVAL;
|
|
|
|
goto bad;
|
|
|
|
}
|
2014-05-28 12:45:27 +00:00
|
|
|
/* enc0: strip outer IPv4 header */
|
|
|
|
m_striphdr(m, 0, ip->ip_hl << 2);
|
2002-10-16 02:10:08 +00:00
|
|
|
}
|
2006-06-04 19:32:32 +00:00
|
|
|
#ifdef INET6
|
2002-10-16 02:10:08 +00:00
|
|
|
/* IPv6-in-IP encapsulation. */
|
2014-11-06 20:23:57 +00:00
|
|
|
else if (prot == IPPROTO_IPV6 &&
|
2014-10-02 02:00:21 +00:00
|
|
|
saidx->mode != IPSEC_MODE_TRANSPORT) {
|
2003-08-13 22:36:24 +00:00
|
|
|
if (m->m_pkthdr.len - skip < sizeof(struct ip6_hdr)) {
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, hdrops);
|
2003-08-13 22:36:24 +00:00
|
|
|
error = EINVAL;
|
|
|
|
goto bad;
|
|
|
|
}
|
2014-05-28 12:45:27 +00:00
|
|
|
/* enc0: strip IPv4 header, keep IPv6 header only */
|
|
|
|
m_striphdr(m, 0, ip->ip_hl << 2);
|
2002-10-16 02:10:08 +00:00
|
|
|
}
|
|
|
|
#endif /* INET6 */
|
2014-11-06 20:23:57 +00:00
|
|
|
else if (prot != IPPROTO_IPV6 && saidx->mode == IPSEC_MODE_ANY) {
|
|
|
|
/*
|
|
|
|
* When mode is wildcard, inner protocol is IPv6 and
|
|
|
|
* we have no INET6 support - drop this packet a bit later.
|
2017-02-06 08:49:57 +00:00
|
|
|
* In other cases we assume transport mode. Set prot to
|
|
|
|
* correctly choose netisr.
|
2014-11-06 20:23:57 +00:00
|
|
|
*/
|
|
|
|
prot = IPPROTO_IPIP;
|
|
|
|
}
|
2002-10-16 02:10:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Record what we've done to the packet (under what SA it was
|
2014-12-11 17:14:49 +00:00
|
|
|
* processed).
|
2002-10-16 02:10:08 +00:00
|
|
|
*/
|
2014-12-11 17:14:49 +00:00
|
|
|
if (sproto != IPPROTO_IPCOMP) {
|
2002-10-16 02:10:08 +00:00
|
|
|
mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE,
|
2017-02-06 08:49:57 +00:00
|
|
|
sizeof(struct xform_history), M_NOWAIT);
|
2002-10-16 02:10:08 +00:00
|
|
|
if (mtag == NULL) {
|
2003-09-29 22:57:43 +00:00
|
|
|
DPRINTF(("%s: failed to get tag\n", __func__));
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, hdrops);
|
2002-10-16 02:10:08 +00:00
|
|
|
error = ENOMEM;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2017-02-06 08:49:57 +00:00
|
|
|
xh = (struct xform_history *)(mtag + 1);
|
|
|
|
bcopy(&saidx->dst, &xh->dst, saidx->dst.sa.sa_len);
|
|
|
|
xh->spi = sav->spi;
|
|
|
|
xh->proto = sproto;
|
|
|
|
xh->mode = saidx->mode;
|
2002-10-16 02:10:08 +00:00
|
|
|
m_tag_prepend(m, mtag);
|
|
|
|
}
|
|
|
|
|
|
|
|
key_sa_recordxfer(sav, m); /* record data transfer */
|
|
|
|
|
2014-10-02 02:00:21 +00:00
|
|
|
/*
|
|
|
|
* In transport mode requeue decrypted mbuf back to IPv4 protocol
|
|
|
|
* handler. This is necessary to correctly expose rcvif.
|
|
|
|
*/
|
|
|
|
if (saidx->mode == IPSEC_MODE_TRANSPORT)
|
|
|
|
prot = IPPROTO_IPIP;
|
2002-10-16 02:10:08 +00:00
|
|
|
/*
|
|
|
|
* Re-dispatch via software interrupt.
|
|
|
|
*/
|
2014-05-28 12:45:27 +00:00
|
|
|
switch (prot) {
|
|
|
|
case IPPROTO_IPIP:
|
|
|
|
isr_prot = NETISR_IP;
|
2015-11-25 07:31:59 +00:00
|
|
|
af = AF_INET;
|
2014-05-28 12:45:27 +00:00
|
|
|
break;
|
|
|
|
#ifdef INET6
|
|
|
|
case IPPROTO_IPV6:
|
|
|
|
isr_prot = NETISR_IPV6;
|
2015-11-25 07:31:59 +00:00
|
|
|
af = AF_INET6;
|
2014-05-28 12:45:27 +00:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
DPRINTF(("%s: cannot handle inner ip proto %d\n",
|
|
|
|
__func__, prot));
|
|
|
|
IPSEC_ISTAT(sproto, nopf);
|
|
|
|
error = EPFNOSUPPORT;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2017-07-31 11:04:35 +00:00
|
|
|
IPSEC_INIT_CTX(&ctx, &m, NULL, sav, af, IPSEC_ENC_AFTER);
|
2015-11-25 07:31:59 +00:00
|
|
|
if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0)
|
|
|
|
goto bad;
|
2017-02-06 08:49:57 +00:00
|
|
|
|
|
|
|
/* Handle virtual tunneling interfaces */
|
|
|
|
if (saidx->mode == IPSEC_MODE_TUNNEL)
|
|
|
|
error = ipsec_if_input(m, sav, af);
|
|
|
|
if (error == 0) {
|
|
|
|
error = netisr_queue_src(isr_prot, (uintptr_t)sav->spi, m);
|
|
|
|
if (error) {
|
|
|
|
IPSEC_ISTAT(sproto, qfull);
|
|
|
|
DPRINTF(("%s: queue full; proto %u packet dropped\n",
|
|
|
|
__func__, sproto));
|
|
|
|
}
|
2002-10-16 02:10:08 +00:00
|
|
|
}
|
2017-02-06 08:49:57 +00:00
|
|
|
key_freesav(&sav);
|
|
|
|
return (error);
|
2002-10-16 02:10:08 +00:00
|
|
|
bad:
|
2017-02-06 08:49:57 +00:00
|
|
|
key_freesav(&sav);
|
|
|
|
if (m != NULL)
|
|
|
|
m_freem(m);
|
|
|
|
return (error);
|
2003-09-29 22:57:43 +00:00
|
|
|
}
|
2002-10-16 02:10:08 +00:00
|
|
|
#endif /* INET */
|
|
|
|
|
|
|
|
#ifdef INET6
|
2017-02-06 08:49:57 +00:00
|
|
|
/*
|
|
|
|
* IPSEC_INPUT() method implementation for IPv6.
|
|
|
|
* 0 - Permitted by inbound security policy for further processing.
|
|
|
|
* EACCES - Forbidden by inbound security policy.
|
|
|
|
* EINPROGRESS - consumed by IPsec.
|
|
|
|
*/
|
2002-10-16 02:10:08 +00:00
|
|
|
int
|
2017-02-06 08:49:57 +00:00
|
|
|
ipsec6_input(struct mbuf *m, int offset, int proto)
|
2002-10-16 02:10:08 +00:00
|
|
|
{
|
2017-02-06 08:49:57 +00:00
|
|
|
|
|
|
|
switch (proto) {
|
|
|
|
case IPPROTO_AH:
|
|
|
|
case IPPROTO_ESP:
|
|
|
|
case IPPROTO_IPCOMP:
|
|
|
|
/* Do inbound IPsec processing for AH/ESP/IPCOMP */
|
|
|
|
ipsec_common_input(m, offset,
|
|
|
|
offsetof(struct ip6_hdr, ip6_nxt), AF_INET6, proto);
|
|
|
|
return (EINPROGRESS); /* mbuf consumed by IPsec */
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* Protocols with further headers get their IPsec treatment
|
|
|
|
* within the protocol specific processing.
|
|
|
|
*/
|
|
|
|
if ((inet6sw[ip6_protox[proto]].pr_flags & PR_LASTHDR) == 0)
|
|
|
|
return (0);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
};
|
|
|
|
/*
|
|
|
|
* Enforce IPsec policy checking if we are seeing last header.
|
|
|
|
*/
|
|
|
|
if (ipsec6_in_reject(m, NULL) != 0) {
|
|
|
|
/* Forbidden by inbound security policy */
|
|
|
|
m_freem(m);
|
|
|
|
return (EACCES);
|
2002-10-16 02:10:08 +00:00
|
|
|
}
|
2017-02-06 08:49:57 +00:00
|
|
|
return (0);
|
2002-10-16 02:10:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IPsec input callback, called by the transform callback. Takes care of
|
|
|
|
* filtering and other sanity checks on the processed packet.
|
|
|
|
*/
|
|
|
|
int
|
2014-12-11 17:14:49 +00:00
|
|
|
ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
|
|
|
|
int protoff)
|
2002-10-16 02:10:08 +00:00
|
|
|
{
|
2017-05-29 09:30:38 +00:00
|
|
|
IPSEC_DEBUG_DECLARE(char buf[IPSEC_ADDRSTRLEN]);
|
2015-11-25 07:31:59 +00:00
|
|
|
struct ipsec_ctx_data ctx;
|
2017-02-06 08:49:57 +00:00
|
|
|
struct xform_history *xh;
|
|
|
|
struct secasindex *saidx;
|
2002-10-16 02:10:08 +00:00
|
|
|
struct ip6_hdr *ip6;
|
|
|
|
struct m_tag *mtag;
|
2017-02-06 08:49:57 +00:00
|
|
|
int prot, af, sproto;
|
2015-04-18 16:51:24 +00:00
|
|
|
int nxt, isr_prot;
|
2002-10-16 02:10:08 +00:00
|
|
|
int error, nest;
|
2017-02-06 08:49:57 +00:00
|
|
|
uint8_t nxt8;
|
2002-10-16 02:10:08 +00:00
|
|
|
|
2003-09-29 22:57:43 +00:00
|
|
|
IPSEC_ASSERT(sav != NULL, ("null SA"));
|
|
|
|
IPSEC_ASSERT(sav->sah != NULL, ("null SAH"));
|
2002-10-16 02:10:08 +00:00
|
|
|
saidx = &sav->sah->saidx;
|
|
|
|
af = saidx->dst.sa.sa_family;
|
2003-09-29 22:57:43 +00:00
|
|
|
IPSEC_ASSERT(af == AF_INET6, ("unexpected af %u", af));
|
2002-10-16 02:10:08 +00:00
|
|
|
sproto = saidx->proto;
|
2003-09-29 22:57:43 +00:00
|
|
|
IPSEC_ASSERT(sproto == IPPROTO_ESP || sproto == IPPROTO_AH ||
|
2002-10-16 02:10:08 +00:00
|
|
|
sproto == IPPROTO_IPCOMP,
|
2003-09-29 22:57:43 +00:00
|
|
|
("unexpected security protocol %u", sproto));
|
2002-10-16 02:10:08 +00:00
|
|
|
|
|
|
|
/* Fix IPv6 header */
|
|
|
|
if (m->m_len < sizeof(struct ip6_hdr) &&
|
|
|
|
(m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
|
|
|
|
|
2003-09-29 22:57:43 +00:00
|
|
|
DPRINTF(("%s: processing failed for SA %s/%08lx\n",
|
2015-04-18 16:58:33 +00:00
|
|
|
__func__, ipsec_address(&sav->sah->saidx.dst, buf,
|
|
|
|
sizeof(buf)), (u_long) ntohl(sav->spi)));
|
2002-10-16 02:10:08 +00:00
|
|
|
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, hdrops);
|
2002-10-16 02:10:08 +00:00
|
|
|
error = EACCES;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2017-07-31 11:04:35 +00:00
|
|
|
IPSEC_INIT_CTX(&ctx, &m, NULL, sav, af, IPSEC_ENC_BEFORE);
|
2015-11-25 07:31:59 +00:00
|
|
|
if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0)
|
|
|
|
goto bad;
|
2017-02-06 08:49:57 +00:00
|
|
|
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
|
|
|
|
|
2002-10-16 02:10:08 +00:00
|
|
|
/* Save protocol */
|
2014-11-13 10:48:59 +00:00
|
|
|
m_copydata(m, protoff, 1, &nxt8);
|
|
|
|
prot = nxt8;
|
2002-10-16 02:10:08 +00:00
|
|
|
|
2014-11-13 10:48:59 +00:00
|
|
|
/* IPv6-in-IP encapsulation */
|
|
|
|
if (prot == IPPROTO_IPV6 &&
|
|
|
|
saidx->mode != IPSEC_MODE_TRANSPORT) {
|
|
|
|
if (m->m_pkthdr.len - skip < sizeof(struct ip6_hdr)) {
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, hdrops);
|
2003-08-13 22:36:24 +00:00
|
|
|
error = EINVAL;
|
|
|
|
goto bad;
|
|
|
|
}
|
2014-11-13 10:48:59 +00:00
|
|
|
/* ip6n will now contain the inner IPv6 header. */
|
|
|
|
m_striphdr(m, 0, skip);
|
2014-05-28 12:45:27 +00:00
|
|
|
skip = 0;
|
2002-10-16 02:10:08 +00:00
|
|
|
}
|
2014-11-13 10:48:59 +00:00
|
|
|
#ifdef INET
|
|
|
|
/* IP-in-IP encapsulation */
|
|
|
|
else if (prot == IPPROTO_IPIP &&
|
|
|
|
saidx->mode != IPSEC_MODE_TRANSPORT) {
|
|
|
|
if (m->m_pkthdr.len - skip < sizeof(struct ip)) {
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, hdrops);
|
2003-08-13 22:36:24 +00:00
|
|
|
error = EINVAL;
|
|
|
|
goto bad;
|
|
|
|
}
|
2014-11-13 10:48:59 +00:00
|
|
|
/* ipn will now contain the inner IPv4 header */
|
2017-02-06 08:49:57 +00:00
|
|
|
m_striphdr(m, 0, skip);
|
2014-05-28 12:45:27 +00:00
|
|
|
skip = 0;
|
2002-10-16 02:10:08 +00:00
|
|
|
}
|
2014-11-13 10:48:59 +00:00
|
|
|
#endif /* INET */
|
|
|
|
else {
|
|
|
|
prot = IPPROTO_IPV6; /* for correct BPF processing */
|
|
|
|
}
|
2002-10-16 02:10:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Record what we've done to the packet (under what SA it was
|
2014-12-11 17:14:49 +00:00
|
|
|
* processed).
|
2002-10-16 02:10:08 +00:00
|
|
|
*/
|
2014-12-11 17:14:49 +00:00
|
|
|
if (sproto != IPPROTO_IPCOMP) {
|
2002-10-16 02:10:08 +00:00
|
|
|
mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE,
|
2017-02-06 08:49:57 +00:00
|
|
|
sizeof(struct xform_history), M_NOWAIT);
|
2002-10-16 02:10:08 +00:00
|
|
|
if (mtag == NULL) {
|
2003-09-29 22:57:43 +00:00
|
|
|
DPRINTF(("%s: failed to get tag\n", __func__));
|
2013-06-20 11:44:16 +00:00
|
|
|
IPSEC_ISTAT(sproto, hdrops);
|
2002-10-16 02:10:08 +00:00
|
|
|
error = ENOMEM;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2017-02-06 08:49:57 +00:00
|
|
|
xh = (struct xform_history *)(mtag + 1);
|
|
|
|
bcopy(&saidx->dst, &xh->dst, saidx->dst.sa.sa_len);
|
|
|
|
xh->spi = sav->spi;
|
|
|
|
xh->proto = sproto;
|
|
|
|
xh->mode = saidx->mode;
|
2002-10-16 02:10:08 +00:00
|
|
|
m_tag_prepend(m, mtag);
|
|
|
|
}
|
|
|
|
|
|
|
|
key_sa_recordxfer(sav, m);
|
|
|
|
|
2014-05-28 12:45:27 +00:00
|
|
|
#ifdef INET
|
|
|
|
if (prot == IPPROTO_IPIP)
|
2015-11-25 07:31:59 +00:00
|
|
|
af = AF_INET;
|
|
|
|
else
|
2007-11-28 22:33:53 +00:00
|
|
|
#endif
|
2015-11-25 07:31:59 +00:00
|
|
|
af = AF_INET6;
|
2017-07-31 11:04:35 +00:00
|
|
|
IPSEC_INIT_CTX(&ctx, &m, NULL, sav, af, IPSEC_ENC_AFTER);
|
2015-11-25 07:31:59 +00:00
|
|
|
if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0)
|
|
|
|
goto bad;
|
2015-04-18 16:51:24 +00:00
|
|
|
if (skip == 0) {
|
|
|
|
/*
|
|
|
|
* We stripped outer IPv6 header.
|
|
|
|
* Now we should requeue decrypted packet via netisr.
|
|
|
|
*/
|
|
|
|
switch (prot) {
|
|
|
|
#ifdef INET
|
|
|
|
case IPPROTO_IPIP:
|
|
|
|
isr_prot = NETISR_IP;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case IPPROTO_IPV6:
|
|
|
|
isr_prot = NETISR_IPV6;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DPRINTF(("%s: cannot handle inner ip proto %d\n",
|
|
|
|
__func__, prot));
|
|
|
|
IPSEC_ISTAT(sproto, nopf);
|
|
|
|
error = EPFNOSUPPORT;
|
|
|
|
goto bad;
|
|
|
|
}
|
2017-02-06 08:49:57 +00:00
|
|
|
/* Handle virtual tunneling interfaces */
|
|
|
|
if (saidx->mode == IPSEC_MODE_TUNNEL)
|
|
|
|
error = ipsec_if_input(m, sav, af);
|
|
|
|
if (error == 0) {
|
|
|
|
error = netisr_queue_src(isr_prot,
|
|
|
|
(uintptr_t)sav->spi, m);
|
|
|
|
if (error) {
|
|
|
|
IPSEC_ISTAT(sproto, qfull);
|
|
|
|
DPRINTF(("%s: queue full; proto %u packet"
|
|
|
|
" dropped\n", __func__, sproto));
|
|
|
|
}
|
2015-04-18 16:51:24 +00:00
|
|
|
}
|
2017-02-06 08:49:57 +00:00
|
|
|
key_freesav(&sav);
|
2015-04-18 16:51:24 +00:00
|
|
|
return (error);
|
|
|
|
}
|
2002-10-16 02:10:08 +00:00
|
|
|
/*
|
|
|
|
* See the end of ip6_input for this logic.
|
|
|
|
* IPPROTO_IPV[46] case will be processed just like other ones
|
|
|
|
*/
|
|
|
|
nest = 0;
|
|
|
|
nxt = nxt8;
|
|
|
|
while (nxt != IPPROTO_DONE) {
|
Commit step 1 of the vimage project, (network stack)
virtualization work done by Marko Zec (zec@).
This is the first in a series of commits over the course
of the next few weeks.
Mark all uses of global variables to be virtualized
with a V_ prefix.
Use macros to map them back to their global names for
now, so this is a NOP change only.
We hope to have caught at least 85-90% of what is needed
so we do not invalidate a lot of outstanding patches again.
Obtained from: //depot/projects/vimage-commit2/...
Reviewed by: brooks, des, ed, mav, julian,
jamie, kris, rwatson, zec, ...
(various people I forgot, different versions)
md5 (with a bit of help)
Sponsored by: NLnet Foundation, The FreeBSD Foundation
X-MFC after: never
V_Commit_Message_Reviewed_By: more people than the patch
2008-08-17 23:27:27 +00:00
|
|
|
if (V_ip6_hdrnestlimit && (++nest > V_ip6_hdrnestlimit)) {
|
2013-04-09 07:11:22 +00:00
|
|
|
IP6STAT_INC(ip6s_toomanyhdr);
|
2002-10-16 02:10:08 +00:00
|
|
|
error = EINVAL;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Protection against faulty packet - there should be
|
|
|
|
* more sanity checks in header chain processing.
|
|
|
|
*/
|
|
|
|
if (m->m_pkthdr.len < skip) {
|
2013-04-09 07:11:22 +00:00
|
|
|
IP6STAT_INC(ip6s_tooshort);
|
2002-10-16 02:10:08 +00:00
|
|
|
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated);
|
|
|
|
error = EINVAL;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Enforce IPsec policy checking if we are seeing last header.
|
|
|
|
* note that we do not visit this with protocols with pcb layer
|
|
|
|
* code - like udp/tcp/raw ip.
|
|
|
|
*/
|
|
|
|
if ((inet6sw[ip6_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
|
|
|
|
ipsec6_in_reject(m, NULL)) {
|
|
|
|
error = EINVAL;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &skip, nxt);
|
|
|
|
}
|
2017-02-06 08:49:57 +00:00
|
|
|
key_freesav(&sav);
|
|
|
|
return (0);
|
2002-10-16 02:10:08 +00:00
|
|
|
bad:
|
2017-02-06 08:49:57 +00:00
|
|
|
key_freesav(&sav);
|
2002-10-16 02:10:08 +00:00
|
|
|
if (m)
|
|
|
|
m_freem(m);
|
2017-02-06 08:49:57 +00:00
|
|
|
return (error);
|
2003-09-29 22:57:43 +00:00
|
|
|
}
|
2002-10-16 02:10:08 +00:00
|
|
|
#endif /* INET6 */
|