if_enc.c revision 196019
1/*-
2 * Copyright (c) 2006 The FreeBSD Project.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/net/if_enc.c 196019 2009-08-01 19:26:27Z rwatson $
28 */
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/malloc.h>
34#include <sys/mbuf.h>
35#include <sys/module.h>
36#include <machine/bus.h>
37#include <sys/rman.h>
38#include <sys/socket.h>
39#include <sys/sockio.h>
40#include <sys/sysctl.h>
41
42#include <net/if.h>
43#include <net/if_clone.h>
44#include <net/if_types.h>
45#include <net/pfil.h>
46#include <net/route.h>
47#include <net/netisr.h>
48#include <net/bpf.h>
49#include <net/vnet.h>
50
51#include <netinet/in.h>
52#include <netinet/in_systm.h>
53#include <netinet/ip.h>
54#include <netinet/ip_var.h>
55#include <netinet/in_var.h>
56#include "opt_inet6.h"
57
58#ifdef INET6
59#include <netinet/ip6.h>
60#include <netinet6/ip6_var.h>
61#endif
62
63#include "opt_enc.h"
64#include <netipsec/ipsec.h>
65#include <netipsec/xform.h>
66
67#define ENCMTU		(1024+512)
68
69/* XXX this define must have the same value as in OpenBSD */
70#define M_CONF		0x0400	/* payload was encrypted (ESP-transport) */
71#define M_AUTH		0x0800	/* payload was authenticated (AH or ESP auth) */
72#define M_AUTH_AH	0x2000	/* header was authenticated (AH) */
73
74struct enchdr {
75	u_int32_t af;
76	u_int32_t spi;
77	u_int32_t flags;
78};
79
80struct ifnet	*encif;
81static struct mtx	enc_mtx;
82
83struct enc_softc {
84	struct	ifnet *sc_ifp;
85};
86
87static int	enc_ioctl(struct ifnet *, u_long, caddr_t);
88static int	enc_output(struct ifnet *ifp, struct mbuf *m,
89		    struct sockaddr *dst, struct route *ro);
90static int	enc_clone_create(struct if_clone *, int, caddr_t);
91static void	enc_clone_destroy(struct ifnet *);
92
93IFC_SIMPLE_DECLARE(enc, 1);
94
95/*
96 * Sysctls.
97 */
98
99/*
100 * Before and after are relative to when we are stripping the
101 * outer IP header.
102 */
103SYSCTL_NODE(_net, OID_AUTO, enc, CTLFLAG_RW, 0, "enc sysctl");
104
105SYSCTL_NODE(_net_enc, OID_AUTO, in, CTLFLAG_RW, 0, "enc input sysctl");
106static int ipsec_filter_mask_in = ENC_BEFORE;
107SYSCTL_XINT(_net_enc_in, OID_AUTO, ipsec_filter_mask, CTLFLAG_RW,
108	&ipsec_filter_mask_in, 0, "IPsec input firewall filter mask");
109static int ipsec_bpf_mask_in = ENC_BEFORE;
110SYSCTL_XINT(_net_enc_in, OID_AUTO, ipsec_bpf_mask, CTLFLAG_RW,
111	&ipsec_bpf_mask_in, 0, "IPsec input bpf mask");
112
113SYSCTL_NODE(_net_enc, OID_AUTO, out, CTLFLAG_RW, 0, "enc output sysctl");
114static int ipsec_filter_mask_out = ENC_BEFORE;
115SYSCTL_XINT(_net_enc_out, OID_AUTO, ipsec_filter_mask, CTLFLAG_RW,
116	&ipsec_filter_mask_out, 0, "IPsec output firewall filter mask");
117static int ipsec_bpf_mask_out = ENC_BEFORE|ENC_AFTER;
118SYSCTL_XINT(_net_enc_out, OID_AUTO, ipsec_bpf_mask, CTLFLAG_RW,
119	&ipsec_bpf_mask_out, 0, "IPsec output bpf mask");
120
121static void
122enc_clone_destroy(struct ifnet *ifp)
123{
124	KASSERT(ifp != encif, ("%s: destroying encif", __func__));
125
126	bpfdetach(ifp);
127	if_detach(ifp);
128	if_free(ifp);
129}
130
131static int
132enc_clone_create(struct if_clone *ifc, int unit, caddr_t params)
133{
134	struct ifnet *ifp;
135	struct enc_softc *sc;
136
137	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
138	ifp = sc->sc_ifp = if_alloc(IFT_ENC);
139	if (ifp == NULL) {
140		free(sc, M_DEVBUF);
141		return (ENOSPC);
142	}
143
144	if_initname(ifp, ifc->ifc_name, unit);
145	ifp->if_mtu = ENCMTU;
146	ifp->if_ioctl = enc_ioctl;
147	ifp->if_output = enc_output;
148	ifp->if_snd.ifq_maxlen = ifqmaxlen;
149	ifp->if_softc = sc;
150	if_attach(ifp);
151	bpfattach(ifp, DLT_ENC, sizeof(struct enchdr));
152
153	mtx_lock(&enc_mtx);
154	/* grab a pointer to enc0, ignore the rest */
155	if (encif == NULL)
156		encif = ifp;
157	mtx_unlock(&enc_mtx);
158
159	return (0);
160}
161
162static int
163enc_modevent(module_t mod, int type, void *data)
164{
165	switch (type) {
166	case MOD_LOAD:
167		mtx_init(&enc_mtx, "enc mtx", NULL, MTX_DEF);
168		if_clone_attach(&enc_cloner);
169		break;
170	case MOD_UNLOAD:
171		printf("enc module unload - not possible for this module\n");
172		return (EINVAL);
173	default:
174		return (EOPNOTSUPP);
175	}
176	return (0);
177}
178
179static moduledata_t enc_mod = {
180	"enc",
181	enc_modevent,
182	0
183};
184
185DECLARE_MODULE(enc, enc_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
186
187static int
188enc_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
189    struct route *ro)
190{
191	m_freem(m);
192	return (0);
193}
194
195/*
196 * Process an ioctl request.
197 */
198/* ARGSUSED */
199static int
200enc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
201{
202	int error = 0;
203
204	mtx_lock(&enc_mtx);
205
206	switch (cmd) {
207
208	case SIOCSIFFLAGS:
209		if (ifp->if_flags & IFF_UP)
210			ifp->if_drv_flags |= IFF_DRV_RUNNING;
211		else
212			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
213
214		break;
215
216	default:
217		error = EINVAL;
218	}
219
220	mtx_unlock(&enc_mtx);
221	return (error);
222}
223
224int
225ipsec_filter(struct mbuf **mp, int dir, int flags)
226{
227	int error, i;
228	struct ip *ip;
229
230	KASSERT(encif != NULL, ("%s: encif is null", __func__));
231	KASSERT(flags & (ENC_IN|ENC_OUT),
232		("%s: invalid flags: %04x", __func__, flags));
233
234	if ((encif->if_drv_flags & IFF_DRV_RUNNING) == 0)
235		return (0);
236
237	if (flags & ENC_IN) {
238		if ((flags & ipsec_filter_mask_in) == 0)
239			return (0);
240	} else {
241		if ((flags & ipsec_filter_mask_out) == 0)
242			return (0);
243	}
244
245	/* Skip pfil(9) if no filters are loaded */
246	if (!(PFIL_HOOKED(&inet_pfil_hook)
247#ifdef INET6
248	    || PFIL_HOOKED(&inet6_pfil_hook)
249#endif
250	    )) {
251		return (0);
252	}
253
254	i = min((*mp)->m_pkthdr.len, max_protohdr);
255	if ((*mp)->m_len < i) {
256		*mp = m_pullup(*mp, i);
257		if (*mp == NULL) {
258			printf("%s: m_pullup failed\n", __func__);
259			return (-1);
260		}
261	}
262
263	error = 0;
264	ip = mtod(*mp, struct ip *);
265	switch (ip->ip_v) {
266		case 4:
267			/*
268			 * before calling the firewall, swap fields the same as
269			 * IP does. here we assume the header is contiguous
270			 */
271			ip->ip_len = ntohs(ip->ip_len);
272			ip->ip_off = ntohs(ip->ip_off);
273
274			error = pfil_run_hooks(&inet_pfil_hook, mp,
275			    encif, dir, NULL);
276
277			if (*mp == NULL || error != 0)
278				break;
279
280			/* restore byte ordering */
281			ip = mtod(*mp, struct ip *);
282			ip->ip_len = htons(ip->ip_len);
283			ip->ip_off = htons(ip->ip_off);
284			break;
285
286#ifdef INET6
287		case 6:
288			error = pfil_run_hooks(&inet6_pfil_hook, mp,
289			    encif, dir, NULL);
290			break;
291#endif
292		default:
293			printf("%s: unknown IP version\n", __func__);
294	}
295
296	/*
297	 * If the mbuf was consumed by the filter for requeueing (dummynet, etc)
298	 * then error will be zero but we still want to return an error to our
299	 * caller so the null mbuf isn't forwarded further.
300	 */
301	if (*mp == NULL && error == 0)
302		return (-1);	/* Consumed by the filter */
303	if (*mp == NULL)
304		return (error);
305	if (error != 0)
306		goto bad;
307
308	return (error);
309
310bad:
311	m_freem(*mp);
312	*mp = NULL;
313	return (error);
314}
315
316void
317ipsec_bpf(struct mbuf *m, struct secasvar *sav, int af, int flags)
318{
319	int mflags;
320	struct enchdr hdr;
321
322	KASSERT(encif != NULL, ("%s: encif is null", __func__));
323	KASSERT(flags & (ENC_IN|ENC_OUT),
324		("%s: invalid flags: %04x", __func__, flags));
325
326	if ((encif->if_drv_flags & IFF_DRV_RUNNING) == 0)
327		return;
328
329	if (flags & ENC_IN) {
330		if ((flags & ipsec_bpf_mask_in) == 0)
331			return;
332	} else {
333		if ((flags & ipsec_bpf_mask_out) == 0)
334			return;
335	}
336
337	if (bpf_peers_present(encif->if_bpf)) {
338		mflags = 0;
339		hdr.spi = 0;
340		if (!sav) {
341			struct m_tag *mtag;
342			mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
343			if (mtag != NULL) {
344				struct tdb_ident *tdbi;
345				tdbi = (struct tdb_ident *) (mtag + 1);
346				if (tdbi->alg_enc != SADB_EALG_NONE)
347					mflags |= M_CONF;
348				if (tdbi->alg_auth != SADB_AALG_NONE)
349					mflags |= M_AUTH;
350				hdr.spi = tdbi->spi;
351			}
352		} else {
353			if (sav->alg_enc != SADB_EALG_NONE)
354				mflags |= M_CONF;
355			if (sav->alg_auth != SADB_AALG_NONE)
356				mflags |= M_AUTH;
357			hdr.spi = sav->spi;
358		}
359
360		/*
361		 * We need to prepend the address family as a four byte
362		 * field.  Cons up a dummy header to pacify bpf.  This
363		 * is safe because bpf will only read from the mbuf
364		 * (i.e., it won't try to free it or keep a pointer a
365		 * to it).
366		 */
367		hdr.af = af;
368		/* hdr.spi already set above */
369		hdr.flags = mflags;
370
371		bpf_mtap2(encif->if_bpf, &hdr, sizeof(hdr), m);
372	}
373}
374