Add a pseudo interface for packet filtering IPSec connections before or after

encryption. There are two functions, a bpf tap which has a basic header with
the SPI number which our current tcpdump knows how to display, and handoff to
pfil(9) for packet filtering.

Obtained from:	OpenBSD
Based on:	kern/94829
No objections:	arch, net
MFC after:	1 month
This commit is contained in:
Andrew Thompson 2006-06-26 22:30:08 +00:00
parent d81175c738
commit bdea400f3b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=159965
10 changed files with 449 additions and 0 deletions

82
share/man/man4/enc.4 Normal file
View File

@ -0,0 +1,82 @@
.\" $OpenBSD: enc.4,v 1.22 2006/05/26 08:51:29 jmc Exp $
.\"
.\" Copyright (c) 1999 Angelos D. Keromytis
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\"
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by Angelos D. Keromytis.
.\" 4. The name of the author may not be used to endorse or promote products
.\" derived from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
.\"
.\" $FreeBSD$
.\"
.Dd June 16, 2006
.Dt ENC 4
.Os
.Sh NAME
.Nm enc
.Nd Encapsulating Interface
.Sh SYNOPSIS
.Cd "device enc"
.Sh DESCRIPTION
The
.Nm
interface is a software loopback mechanism that allows hosts or
firewalls to filter
.Xr fast_ipsec 4
traffic using any firewall package that hooks in via the
.Xr pfil 9
framework.
.Pp
The
.Nm
interface allows an administrator
to see outgoing packets before they have been processed by
.Xr fast_ipsec 4 ,
or incoming packets after they have been similarly processed, via
.Xr tcpdump 8 .
.Pp
The
.Dq enc0
interface inherits all IPsec traffic.
Thus all IPsec traffic can be filtered based on
.Dq enc0 ,
and all IPsec traffic could be seen by invoking
.Xr tcpdump 8
on the
.Dq enc0
interface.
.Sh EXAMPLES
To see all outgoing packets before they have been processed via
.Xr fast_ipsec 4 ,
or all incoming packets after they have been similarly processed:
.Pp
.Dl # tcpdump -i enc0
.Sh SEE ALSO
.Xr bpf 4 ,
.Xr fast_ipsec 4 ,
.Xr ipf 4 ,
.Xr ipfw 4 ,
.Xr pf 4 ,
.Xr tcpdump 8

View File

@ -78,10 +78,16 @@ When the
protocols are configured for use, all protocols are included in the system.
To selectively enable/disable protocols, use
.Xr sysctl 8 .
.Pp
The packets can be passed to a virtual interface,
.Dq enc0 ,
to perform packet filtering before outbound encryption and after decapsulation
inbound.
.Sh DIAGNOSTICS
To be added.
.Sh SEE ALSO
.Xr crypto 4 ,
.Xr enc 4 ,
.Xr ipsec 4 ,
.Xr setkey 8 ,
.Xr sysctl 8

View File

@ -1459,6 +1459,7 @@ net/if_bridge.c optional if_bridge
net/if_clone.c standard
net/if_disc.c optional disc
net/if_ef.c optional ef
net/if_enc.c optional enc
net/if_ethersubr.c optional ether
net/if_faith.c optional faith
net/if_fddisubr.c optional fddi

View File

@ -340,6 +340,7 @@ BOOTP_NFSROOT opt_bootp.h
BOOTP_NFSV3 opt_bootp.h
BOOTP_WIRED_TO opt_bootp.h
DEVICE_POLLING
DEV_ENC opt_enc.h
DEV_PF opt_pf.h
DEV_PFLOG opt_pf.h
DEV_PFSYNC opt_pf.h

323
sys/net/if_enc.c Normal file
View File

@ -0,0 +1,323 @@
/*-
* Copyright (c) 2006 The FreeBSD Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_clone.h>
#include <net/if_types.h>
#include <net/pfil.h>
#include <net/route.h>
#include <net/netisr.h>
#include <net/bpf.h>
#include <net/bpfdesc.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 "opt_inet6.h"
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#endif
#include <netipsec/ipsec.h>
#define ENCMTU (1024+512)
#define ENC_HDRLEN 12
/* XXX this define must have the same value as in OpenBSD */
#define M_CONF 0x0400 /* payload was encrypted (ESP-transport) */
#define M_AUTH 0x0800 /* payload was authenticated (AH or ESP auth) */
#define M_AUTH_AH 0x2000 /* header was authenticated (AH) */
struct enchdr {
u_int32_t af;
u_int32_t spi;
u_int32_t flags;
};
static struct ifnet *encif;
static struct mtx enc_mtx;
struct enc_softc {
struct ifnet *sc_ifp;
};
static int enc_ioctl(struct ifnet *, u_long, caddr_t);
static int enc_output(struct ifnet *ifp, struct mbuf *m,
struct sockaddr *dst, struct rtentry *rt);
static int enc_clone_create(struct if_clone *, int);
static void enc_clone_destroy(struct ifnet *);
IFC_SIMPLE_DECLARE(enc, 1);
static void
enc_clone_destroy(struct ifnet *ifp)
{
KASSERT(encif == ifp, ("%s: unknown ifnet", __func__));
mtx_lock(&enc_mtx);
encif = NULL;
mtx_unlock(&enc_mtx);
bpfdetach(ifp);
if_detach(ifp);
if_free(ifp);
}
static int
enc_clone_create(struct if_clone *ifc, int unit)
{
struct ifnet *ifp;
struct enc_softc *sc;
mtx_lock(&enc_mtx);
if (encif != NULL)
return (EBUSY);
mtx_unlock(&enc_mtx);
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
ifp = sc->sc_ifp = if_alloc(IFT_ENC);
if (ifp == NULL) {
free(sc, M_DEVBUF);
return (ENOSPC);
}
if_initname(ifp, ifc->ifc_name, unit);
ifp->if_mtu = ENCMTU;
ifp->if_ioctl = enc_ioctl;
ifp->if_output = enc_output;
ifp->if_snd.ifq_maxlen = ifqmaxlen;
ifp->if_softc = sc;
if_attach(ifp);
bpfattach(ifp, DLT_ENC, ENC_HDRLEN);
mtx_lock(&enc_mtx);
encif = ifp;
mtx_unlock(&enc_mtx);
return (0);
}
static int
enc_modevent(module_t mod, int type, void *data)
{
switch (type) {
case MOD_LOAD:
mtx_init(&enc_mtx, "enc mtx", NULL, MTX_DEF);
if_clone_attach(&enc_cloner);
break;
case MOD_UNLOAD:
printf("enc module unload - not possible for this module\n");
return (EINVAL);
default:
return (EOPNOTSUPP);
}
return (0);
}
static moduledata_t enc_mod = {
"enc",
enc_modevent,
0
};
DECLARE_MODULE(enc, enc_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
static int
enc_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
m_freem(m);
return (0);
}
/*
* Process an ioctl request.
*/
/* ARGSUSED */
static int
enc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
int error = 0;
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
else
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
break;
default:
error = EINVAL;
}
return (error);
}
int
ipsec_filter(struct mbuf **mp, int dir)
{
int error, i;
struct ip *ip;
mtx_lock(&enc_mtx);
if (encif == NULL || (encif->if_drv_flags & IFF_DRV_RUNNING) == 0) {
mtx_unlock(&enc_mtx);
return (0);
}
/* Skip pfil(9) if no filters are loaded */
if (!(PFIL_HOOKED(&inet_pfil_hook)
#ifdef INET6
|| PFIL_HOOKED(&inet6_pfil_hook)
#endif
)) {
mtx_unlock(&enc_mtx);
return (0);
}
i = min((*mp)->m_pkthdr.len, max_protohdr);
if ((*mp)->m_len < i) {
*mp = m_pullup(*mp, i);
if (*mp == NULL) {
printf("%s: m_pullup failed\n", __func__);
mtx_unlock(&enc_mtx);
return (-1);
}
}
error = 0;
ip = mtod(*mp, struct ip *);
switch (ip->ip_v) {
case 4:
/*
* before calling the firewall, swap fields the same as
* IP does. here we assume the header is contiguous
*/
ip->ip_len = ntohs(ip->ip_len);
ip->ip_off = ntohs(ip->ip_off);
error = pfil_run_hooks(&inet_pfil_hook, mp,
encif, dir, NULL);
if (*mp == NULL || error != 0)
break;
/* restore byte ordering */
ip = mtod(*mp, struct ip *);
ip->ip_len = htons(ip->ip_len);
ip->ip_off = htons(ip->ip_off);
break;
#ifdef INET6
case 6:
error = pfil_run_hooks(&inet6_pfil_hook, mp,
encif, dir, NULL);
break;
#endif
default:
printf("%s: unknown IP version\n", __func__);
}
mtx_unlock(&enc_mtx);
if (*mp == NULL)
return (error);
if (error != 0)
goto bad;
return (error);
bad:
mtx_unlock(&enc_mtx);
m_freem(*mp);
*mp = NULL;
return (error);
}
void
ipsec_bpf(struct mbuf *m, struct secasvar *sav, int af)
{
int flags;
struct enchdr hdr;
struct mbuf m1;
KASSERT(sav != NULL, ("%s: sav is null", __func__));
mtx_lock(&enc_mtx);
if (encif == NULL || (encif->if_drv_flags & IFF_DRV_RUNNING) == 0) {
mtx_unlock(&enc_mtx);
return;
}
if (encif->if_bpf) {
flags = 0;
if (sav->alg_enc != SADB_EALG_NONE)
flags |= M_CONF;
if (sav->alg_auth != SADB_AALG_NONE)
flags |= M_AUTH;
/*
* We need to prepend the address family as a four byte
* field. Cons up a dummy header to pacify bpf. This
* is safe because bpf will only read from the mbuf
* (i.e., it won't try to free it or keep a pointer a
* to it).
*/
hdr.af = af;
hdr.spi = sav->spi;
hdr.flags = flags;
m1.m_flags = 0;
m1.m_next = m;
m1.m_len = ENC_HDRLEN;
m1.m_data = (char *) &hdr;
bpf_mtap(encif->if_bpf, &m1);
}
mtx_unlock(&enc_mtx);
}

View File

@ -246,6 +246,7 @@
#define IFT_GIF 0xf0
#define IFT_PVC 0xf1
#define IFT_FAITH 0xf2
#define IFT_ENC 0xf4
#define IFT_PFLOG 0xf6
#define IFT_PFSYNC 0xf7
#define IFT_CARP 0xf8 /* Common Address Redundancy Protocol */

View File

@ -417,6 +417,8 @@ extern void m_checkalignment(const char* where, struct mbuf *m0,
extern struct mbuf *m_makespace(struct mbuf *m0, int skip, int hlen, int *off);
extern caddr_t m_pad(struct mbuf *m, int n);
extern int m_striphdr(struct mbuf *m, int skip, int hlen);
extern int ipsec_filter(struct mbuf **, int);
extern void ipsec_bpf(struct mbuf *, struct secasvar *, int);
#endif /* _KERNEL */
#ifndef _KERNEL

View File

@ -43,6 +43,7 @@
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include "opt_enc.h"
#include <sys/param.h>
#include <sys/systm.h>
@ -442,6 +443,18 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav,
key_sa_recordxfer(sav, m); /* record data transfer */
#ifdef DEV_ENC
/*
* Pass the mbuf to enc0 for bpf and pfil. We will filter the IPIP
* packet later after it has been decapsulated.
*/
ipsec_bpf(m, sav, AF_INET);
if (prot != IPPROTO_IPIP)
if ((error = ipsec_filter(&m, 1)) != 0)
return (error);
#endif
/*
* Re-dispatch via software interrupt.
*/

View File

@ -32,6 +32,7 @@
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include "opt_enc.h"
#include <sys/param.h>
#include <sys/systm.h>
@ -358,6 +359,13 @@ ipsec4_process_packet(
goto bad;
sav = isr->sav;
#ifdef DEV_ENC
/* pass the mbuf to enc0 for packet filtering */
if ((error = ipsec_filter(&m, 2)) != 0)
goto bad;
#endif
if (!tunalready) {
union sockaddr_union *dst = &sav->sah->saidx.dst;
int setdf;
@ -455,6 +463,11 @@ ipsec4_process_packet(
}
}
#ifdef DEV_ENC
/* pass the mbuf to enc0 for bpf processing */
ipsec_bpf(m, sav, AF_INET);
#endif
/*
* Dispatch to the appropriate IPsec transform logic. The
* packet will be returned for transmission after crypto

View File

@ -41,6 +41,7 @@
*/
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_enc.h"
#include <sys/param.h>
#include <sys/systm.h>
@ -345,6 +346,12 @@ _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp)
/* Statistics */
ipipstat.ipips_ibytes += m->m_pkthdr.len - iphlen;
#ifdef DEV_ENC
/* pass the mbuf to enc0 for packet filtering */
if (ipsec_filter(&m, 1) != 0)
return;
#endif
/*
* Interface pointer stays the same; if no IPsec processing has
* been done (or will be done), this will point to a normal