1159965Sthompsa/*-
2159965Sthompsa * Copyright (c) 2006 The FreeBSD Project.
3159965Sthompsa * All rights reserved.
4159965Sthompsa *
5159965Sthompsa * Redistribution and use in source and binary forms, with or without
6159965Sthompsa * modification, are permitted provided that the following conditions
7159965Sthompsa * are met:
8159965Sthompsa *
9159965Sthompsa * 1. Redistributions of source code must retain the above copyright
10159965Sthompsa *    notice, this list of conditions and the following disclaimer.
11159965Sthompsa * 2. Redistributions in binary form must reproduce the above copyright
12159965Sthompsa *    notice, this list of conditions and the following disclaimer in the
13159965Sthompsa *    documentation and/or other materials provided with the distribution.
14159965Sthompsa *
15159965Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16159965Sthompsa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17159965Sthompsa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18159965Sthompsa * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19159965Sthompsa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20159965Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21159965Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22159965Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23159965Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24159965Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25159965Sthompsa * SUCH DAMAGE.
26159965Sthompsa *
27159965Sthompsa * $FreeBSD: releng/10.3/sys/net/if_enc.c 255926 2013-09-28 14:14:23Z glebius $
28159965Sthompsa */
29159965Sthompsa
30221130Sbz#include "opt_inet.h"
31221130Sbz#include "opt_inet6.h"
32221130Sbz#include "opt_enc.h"
33221130Sbz
34159965Sthompsa#include <sys/param.h>
35159965Sthompsa#include <sys/systm.h>
36159965Sthompsa#include <sys/kernel.h>
37159965Sthompsa#include <sys/malloc.h>
38159965Sthompsa#include <sys/mbuf.h>
39159965Sthompsa#include <sys/module.h>
40159965Sthompsa#include <machine/bus.h>
41159965Sthompsa#include <sys/rman.h>
42159965Sthompsa#include <sys/socket.h>
43159965Sthompsa#include <sys/sockio.h>
44159965Sthompsa#include <sys/sysctl.h>
45159965Sthompsa
46159965Sthompsa#include <net/if.h>
47159965Sthompsa#include <net/if_clone.h>
48159965Sthompsa#include <net/if_types.h>
49159965Sthompsa#include <net/pfil.h>
50159965Sthompsa#include <net/route.h>
51159965Sthompsa#include <net/netisr.h>
52159965Sthompsa#include <net/bpf.h>
53195699Srwatson#include <net/vnet.h>
54159965Sthompsa
55159965Sthompsa#include <netinet/in.h>
56159965Sthompsa#include <netinet/in_systm.h>
57159965Sthompsa#include <netinet/ip.h>
58159965Sthompsa#include <netinet/ip_var.h>
59159965Sthompsa#include <netinet/in_var.h>
60159965Sthompsa
61159965Sthompsa#ifdef INET6
62159965Sthompsa#include <netinet/ip6.h>
63159965Sthompsa#include <netinet6/ip6_var.h>
64159965Sthompsa#endif
65159965Sthompsa
66159965Sthompsa#include <netipsec/ipsec.h>
67174054Sbz#include <netipsec/xform.h>
68159965Sthompsa
69159965Sthompsa#define ENCMTU		(1024+512)
70159965Sthompsa
71159965Sthompsa/* XXX this define must have the same value as in OpenBSD */
72159965Sthompsa#define M_CONF		0x0400	/* payload was encrypted (ESP-transport) */
73159965Sthompsa#define M_AUTH		0x0800	/* payload was authenticated (AH or ESP auth) */
74159965Sthompsa#define M_AUTH_AH	0x2000	/* header was authenticated (AH) */
75159965Sthompsa
76159965Sthompsastruct enchdr {
77159965Sthompsa	u_int32_t af;
78159965Sthompsa	u_int32_t spi;
79159965Sthompsa	u_int32_t flags;
80159965Sthompsa};
81159965Sthompsa
82181627Svanhustruct ifnet	*encif;
83159965Sthompsastatic struct mtx	enc_mtx;
84159965Sthompsa
85159965Sthompsastruct enc_softc {
86159965Sthompsa	struct	ifnet *sc_ifp;
87159965Sthompsa};
88159965Sthompsa
89159965Sthompsastatic int	enc_ioctl(struct ifnet *, u_long, caddr_t);
90159965Sthompsastatic int	enc_output(struct ifnet *ifp, struct mbuf *m,
91249925Sglebius		    const struct sockaddr *dst, struct route *ro);
92160233Sthompsastatic int	enc_clone_create(struct if_clone *, int, caddr_t);
93160099Sthompsastatic void	enc_clone_destroy(struct ifnet *);
94241610Sglebiusstatic struct if_clone *enc_cloner;
95241610Sglebiusstatic const char encname[] = "enc";
96159965Sthompsa
97174054Sbz/*
98174054Sbz * Sysctls.
99174054Sbz */
100174054Sbz
101174054Sbz/*
102174054Sbz * Before and after are relative to when we are stripping the
103174054Sbz * outer IP header.
104174054Sbz */
105227309Sedstatic SYSCTL_NODE(_net, OID_AUTO, enc, CTLFLAG_RW, 0, "enc sysctl");
106174054Sbz
107227309Sedstatic SYSCTL_NODE(_net_enc, OID_AUTO, in, CTLFLAG_RW, 0, "enc input sysctl");
108174054Sbzstatic int ipsec_filter_mask_in = ENC_BEFORE;
109217586SmdfSYSCTL_INT(_net_enc_in, OID_AUTO, ipsec_filter_mask, CTLFLAG_RW,
110174054Sbz	&ipsec_filter_mask_in, 0, "IPsec input firewall filter mask");
111174054Sbzstatic int ipsec_bpf_mask_in = ENC_BEFORE;
112217586SmdfSYSCTL_INT(_net_enc_in, OID_AUTO, ipsec_bpf_mask, CTLFLAG_RW,
113174054Sbz	&ipsec_bpf_mask_in, 0, "IPsec input bpf mask");
114174054Sbz
115227309Sedstatic SYSCTL_NODE(_net_enc, OID_AUTO, out, CTLFLAG_RW, 0, "enc output sysctl");
116174054Sbzstatic int ipsec_filter_mask_out = ENC_BEFORE;
117217586SmdfSYSCTL_INT(_net_enc_out, OID_AUTO, ipsec_filter_mask, CTLFLAG_RW,
118174054Sbz	&ipsec_filter_mask_out, 0, "IPsec output firewall filter mask");
119174054Sbzstatic int ipsec_bpf_mask_out = ENC_BEFORE|ENC_AFTER;
120217586SmdfSYSCTL_INT(_net_enc_out, OID_AUTO, ipsec_bpf_mask, CTLFLAG_RW,
121174054Sbz	&ipsec_bpf_mask_out, 0, "IPsec output bpf mask");
122174054Sbz
123160099Sthompsastatic void
124159965Sthompsaenc_clone_destroy(struct ifnet *ifp)
125159965Sthompsa{
126160099Sthompsa	KASSERT(ifp != encif, ("%s: destroying encif", __func__));
127159965Sthompsa
128159965Sthompsa	bpfdetach(ifp);
129159965Sthompsa	if_detach(ifp);
130159965Sthompsa	if_free(ifp);
131159965Sthompsa}
132159965Sthompsa
133159965Sthompsastatic int
134160233Sthompsaenc_clone_create(struct if_clone *ifc, int unit, caddr_t params)
135159965Sthompsa{
136159965Sthompsa	struct ifnet *ifp;
137159965Sthompsa	struct enc_softc *sc;
138159965Sthompsa
139159965Sthompsa	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
140159965Sthompsa	ifp = sc->sc_ifp = if_alloc(IFT_ENC);
141159965Sthompsa	if (ifp == NULL) {
142159965Sthompsa		free(sc, M_DEVBUF);
143159965Sthompsa		return (ENOSPC);
144159965Sthompsa	}
145159965Sthompsa
146241610Sglebius	if_initname(ifp, encname, unit);
147159965Sthompsa	ifp->if_mtu = ENCMTU;
148159965Sthompsa	ifp->if_ioctl = enc_ioctl;
149159965Sthompsa	ifp->if_output = enc_output;
150159965Sthompsa	ifp->if_snd.ifq_maxlen = ifqmaxlen;
151159965Sthompsa	ifp->if_softc = sc;
152159965Sthompsa	if_attach(ifp);
153159969Sthompsa	bpfattach(ifp, DLT_ENC, sizeof(struct enchdr));
154159965Sthompsa
155159965Sthompsa	mtx_lock(&enc_mtx);
156160011Sthompsa	/* grab a pointer to enc0, ignore the rest */
157160011Sthompsa	if (encif == NULL)
158160011Sthompsa		encif = ifp;
159159965Sthompsa	mtx_unlock(&enc_mtx);
160159965Sthompsa
161159965Sthompsa	return (0);
162159965Sthompsa}
163159965Sthompsa
164159965Sthompsastatic int
165159965Sthompsaenc_modevent(module_t mod, int type, void *data)
166159965Sthompsa{
167159965Sthompsa	switch (type) {
168159965Sthompsa	case MOD_LOAD:
169159965Sthompsa		mtx_init(&enc_mtx, "enc mtx", NULL, MTX_DEF);
170241610Sglebius		enc_cloner = if_clone_simple(encname, enc_clone_create,
171255926Sglebius		    enc_clone_destroy, 1);
172159965Sthompsa		break;
173159965Sthompsa	case MOD_UNLOAD:
174159965Sthompsa		printf("enc module unload - not possible for this module\n");
175159965Sthompsa		return (EINVAL);
176159965Sthompsa	default:
177159965Sthompsa		return (EOPNOTSUPP);
178159965Sthompsa	}
179159965Sthompsa	return (0);
180159965Sthompsa}
181159965Sthompsa
182159965Sthompsastatic moduledata_t enc_mod = {
183241130Sjhb	"if_enc",
184159965Sthompsa	enc_modevent,
185241394Skevlo	0
186159965Sthompsa};
187159965Sthompsa
188241130SjhbDECLARE_MODULE(if_enc, enc_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
189159965Sthompsa
190159965Sthompsastatic int
191249925Sglebiusenc_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
192191148Skmacy    struct route *ro)
193159965Sthompsa{
194159965Sthompsa	m_freem(m);
195159965Sthompsa	return (0);
196159965Sthompsa}
197159965Sthompsa
198159965Sthompsa/*
199159965Sthompsa * Process an ioctl request.
200159965Sthompsa */
201159965Sthompsa/* ARGSUSED */
202159965Sthompsastatic int
203159965Sthompsaenc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
204159965Sthompsa{
205159965Sthompsa	int error = 0;
206159965Sthompsa
207160011Sthompsa	mtx_lock(&enc_mtx);
208160011Sthompsa
209159965Sthompsa	switch (cmd) {
210159965Sthompsa
211159965Sthompsa	case SIOCSIFFLAGS:
212159965Sthompsa		if (ifp->if_flags & IFF_UP)
213159965Sthompsa			ifp->if_drv_flags |= IFF_DRV_RUNNING;
214159965Sthompsa		else
215159965Sthompsa			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
216159965Sthompsa
217159965Sthompsa		break;
218159965Sthompsa
219159965Sthompsa	default:
220159965Sthompsa		error = EINVAL;
221159965Sthompsa	}
222160011Sthompsa
223160011Sthompsa	mtx_unlock(&enc_mtx);
224159965Sthompsa	return (error);
225159965Sthompsa}
226159965Sthompsa
227159965Sthompsaint
228174054Sbzipsec_filter(struct mbuf **mp, int dir, int flags)
229159965Sthompsa{
230159965Sthompsa	int error, i;
231159965Sthompsa	struct ip *ip;
232159965Sthompsa
233160011Sthompsa	KASSERT(encif != NULL, ("%s: encif is null", __func__));
234174054Sbz	KASSERT(flags & (ENC_IN|ENC_OUT),
235174054Sbz		("%s: invalid flags: %04x", __func__, flags));
236160011Sthompsa
237160011Sthompsa	if ((encif->if_drv_flags & IFF_DRV_RUNNING) == 0)
238159965Sthompsa		return (0);
239159965Sthompsa
240174054Sbz	if (flags & ENC_IN) {
241174054Sbz		if ((flags & ipsec_filter_mask_in) == 0)
242174054Sbz			return (0);
243174054Sbz	} else {
244174054Sbz		if ((flags & ipsec_filter_mask_out) == 0)
245174054Sbz			return (0);
246174054Sbz	}
247174054Sbz
248159965Sthompsa	/* Skip pfil(9) if no filters are loaded */
249221130Sbz	if (1
250221130Sbz#ifdef INET
251221130Sbz	    && !PFIL_HOOKED(&V_inet_pfil_hook)
252221130Sbz#endif
253159965Sthompsa#ifdef INET6
254221130Sbz	    && !PFIL_HOOKED(&V_inet6_pfil_hook)
255159965Sthompsa#endif
256221130Sbz	    ) {
257159965Sthompsa		return (0);
258159965Sthompsa	}
259159965Sthompsa
260159965Sthompsa	i = min((*mp)->m_pkthdr.len, max_protohdr);
261159965Sthompsa	if ((*mp)->m_len < i) {
262159965Sthompsa		*mp = m_pullup(*mp, i);
263159965Sthompsa		if (*mp == NULL) {
264159965Sthompsa			printf("%s: m_pullup failed\n", __func__);
265159965Sthompsa			return (-1);
266159965Sthompsa		}
267159965Sthompsa	}
268159965Sthompsa
269159965Sthompsa	error = 0;
270159965Sthompsa	ip = mtod(*mp, struct ip *);
271159965Sthompsa	switch (ip->ip_v) {
272221130Sbz#ifdef INET
273159965Sthompsa		case 4:
274198075Sbz			error = pfil_run_hooks(&V_inet_pfil_hook, mp,
275159965Sthompsa			    encif, dir, NULL);
276159965Sthompsa			break;
277221130Sbz#endif
278159965Sthompsa#ifdef INET6
279159965Sthompsa		case 6:
280198075Sbz			error = pfil_run_hooks(&V_inet6_pfil_hook, mp,
281159965Sthompsa			    encif, dir, NULL);
282159965Sthompsa			break;
283159965Sthompsa#endif
284159965Sthompsa		default:
285159965Sthompsa			printf("%s: unknown IP version\n", __func__);
286159965Sthompsa	}
287159965Sthompsa
288174913Sthompsa	/*
289174913Sthompsa	 * If the mbuf was consumed by the filter for requeueing (dummynet, etc)
290174913Sthompsa	 * then error will be zero but we still want to return an error to our
291174913Sthompsa	 * caller so the null mbuf isn't forwarded further.
292174913Sthompsa	 */
293174913Sthompsa	if (*mp == NULL && error == 0)
294174913Sthompsa		return (-1);	/* Consumed by the filter */
295159965Sthompsa	if (*mp == NULL)
296159965Sthompsa		return (error);
297159965Sthompsa	if (error != 0)
298159965Sthompsa		goto bad;
299159965Sthompsa
300159965Sthompsa	return (error);
301159965Sthompsa
302159965Sthompsabad:
303159965Sthompsa	m_freem(*mp);
304159965Sthompsa	*mp = NULL;
305159965Sthompsa	return (error);
306159965Sthompsa}
307159965Sthompsa
308159965Sthompsavoid
309174054Sbzipsec_bpf(struct mbuf *m, struct secasvar *sav, int af, int flags)
310159965Sthompsa{
311174054Sbz	int mflags;
312159965Sthompsa	struct enchdr hdr;
313159965Sthompsa
314160011Sthompsa	KASSERT(encif != NULL, ("%s: encif is null", __func__));
315174054Sbz	KASSERT(flags & (ENC_IN|ENC_OUT),
316174054Sbz		("%s: invalid flags: %04x", __func__, flags));
317159965Sthompsa
318160011Sthompsa	if ((encif->if_drv_flags & IFF_DRV_RUNNING) == 0)
319159965Sthompsa		return;
320159965Sthompsa
321174054Sbz	if (flags & ENC_IN) {
322174054Sbz		if ((flags & ipsec_bpf_mask_in) == 0)
323174054Sbz			return;
324174054Sbz	} else {
325174054Sbz		if ((flags & ipsec_bpf_mask_out) == 0)
326174054Sbz			return;
327174054Sbz	}
328174054Sbz
329165632Sjhb	if (bpf_peers_present(encif->if_bpf)) {
330174054Sbz		mflags = 0;
331174054Sbz		hdr.spi = 0;
332174054Sbz		if (!sav) {
333174054Sbz			struct m_tag *mtag;
334174054Sbz			mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
335174054Sbz			if (mtag != NULL) {
336174054Sbz				struct tdb_ident *tdbi;
337174054Sbz				tdbi = (struct tdb_ident *) (mtag + 1);
338174054Sbz				if (tdbi->alg_enc != SADB_EALG_NONE)
339174054Sbz					mflags |= M_CONF;
340174054Sbz				if (tdbi->alg_auth != SADB_AALG_NONE)
341174054Sbz					mflags |= M_AUTH;
342174054Sbz				hdr.spi = tdbi->spi;
343174054Sbz			}
344174054Sbz		} else {
345174054Sbz			if (sav->alg_enc != SADB_EALG_NONE)
346174054Sbz				mflags |= M_CONF;
347174054Sbz			if (sav->alg_auth != SADB_AALG_NONE)
348174054Sbz				mflags |= M_AUTH;
349174054Sbz			hdr.spi = sav->spi;
350174054Sbz		}
351159965Sthompsa
352159965Sthompsa		/*
353159965Sthompsa		 * We need to prepend the address family as a four byte
354159965Sthompsa		 * field.  Cons up a dummy header to pacify bpf.  This
355159965Sthompsa		 * is safe because bpf will only read from the mbuf
356159965Sthompsa		 * (i.e., it won't try to free it or keep a pointer a
357159965Sthompsa		 * to it).
358159965Sthompsa		 */
359159965Sthompsa		hdr.af = af;
360174054Sbz		/* hdr.spi already set above */
361174054Sbz		hdr.flags = mflags;
362159965Sthompsa
363159969Sthompsa		bpf_mtap2(encif->if_bpf, &hdr, sizeof(hdr), m);
364159965Sthompsa	}
365159965Sthompsa}
366