if_enc.c revision 159969
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 159969 2006-06-27 01:53:12Z thompsa $
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/bpfdesc.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 <netipsec/ipsec.h>
64
65#define ENCMTU		(1024+512)
66
67/* XXX this define must have the same value as in OpenBSD */
68#define M_CONF		0x0400	/* payload was encrypted (ESP-transport) */
69#define M_AUTH		0x0800	/* payload was authenticated (AH or ESP auth) */
70#define M_AUTH_AH	0x2000	/* header was authenticated (AH) */
71
72struct enchdr {
73	u_int32_t af;
74	u_int32_t spi;
75	u_int32_t flags;
76};
77
78static struct ifnet	*encif;
79static struct mtx	enc_mtx;
80
81struct enc_softc {
82	struct	ifnet *sc_ifp;
83};
84
85static int	enc_ioctl(struct ifnet *, u_long, caddr_t);
86static int	enc_output(struct ifnet *ifp, struct mbuf *m,
87		    struct sockaddr *dst, struct rtentry *rt);
88static int	enc_clone_create(struct if_clone *, int);
89static void	enc_clone_destroy(struct ifnet *);
90
91IFC_SIMPLE_DECLARE(enc, 1);
92
93static void
94enc_clone_destroy(struct ifnet *ifp)
95{
96
97	KASSERT(encif == ifp, ("%s: unknown ifnet", __func__));
98
99	mtx_lock(&enc_mtx);
100	encif = NULL;
101	mtx_unlock(&enc_mtx);
102
103	bpfdetach(ifp);
104	if_detach(ifp);
105	if_free(ifp);
106
107}
108
109static int
110enc_clone_create(struct if_clone *ifc, int unit)
111{
112	struct ifnet *ifp;
113	struct enc_softc *sc;
114
115	mtx_lock(&enc_mtx);
116	if (encif != NULL)
117		return (EBUSY);
118	mtx_unlock(&enc_mtx);
119
120	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
121	ifp = sc->sc_ifp = if_alloc(IFT_ENC);
122	if (ifp == NULL) {
123		free(sc, M_DEVBUF);
124		return (ENOSPC);
125	}
126
127	if_initname(ifp, ifc->ifc_name, unit);
128	ifp->if_mtu = ENCMTU;
129	ifp->if_ioctl = enc_ioctl;
130	ifp->if_output = enc_output;
131	ifp->if_snd.ifq_maxlen = ifqmaxlen;
132	ifp->if_softc = sc;
133	if_attach(ifp);
134	bpfattach(ifp, DLT_ENC, sizeof(struct enchdr));
135
136	mtx_lock(&enc_mtx);
137	encif = ifp;
138	mtx_unlock(&enc_mtx);
139
140	return (0);
141}
142
143static int
144enc_modevent(module_t mod, int type, void *data)
145{
146	switch (type) {
147	case MOD_LOAD:
148		mtx_init(&enc_mtx, "enc mtx", NULL, MTX_DEF);
149		if_clone_attach(&enc_cloner);
150		break;
151	case MOD_UNLOAD:
152		printf("enc module unload - not possible for this module\n");
153		return (EINVAL);
154	default:
155		return (EOPNOTSUPP);
156	}
157	return (0);
158}
159
160static moduledata_t enc_mod = {
161	"enc",
162	enc_modevent,
163	0
164};
165
166DECLARE_MODULE(enc, enc_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
167
168static int
169enc_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
170    struct rtentry *rt)
171{
172	m_freem(m);
173	return (0);
174}
175
176/*
177 * Process an ioctl request.
178 */
179/* ARGSUSED */
180static int
181enc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
182{
183	int error = 0;
184
185	switch (cmd) {
186
187	case SIOCSIFFLAGS:
188		if (ifp->if_flags & IFF_UP)
189			ifp->if_drv_flags |= IFF_DRV_RUNNING;
190		else
191			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
192
193		break;
194
195	default:
196		error = EINVAL;
197	}
198	return (error);
199}
200
201int
202ipsec_filter(struct mbuf **mp, int dir)
203{
204	int error, i;
205	struct ip *ip;
206
207	mtx_lock(&enc_mtx);
208	if (encif == NULL || (encif->if_drv_flags & IFF_DRV_RUNNING) == 0) {
209		mtx_unlock(&enc_mtx);
210		return (0);
211	}
212
213	/* Skip pfil(9) if no filters are loaded */
214	if (!(PFIL_HOOKED(&inet_pfil_hook)
215#ifdef INET6
216	    || PFIL_HOOKED(&inet6_pfil_hook)
217#endif
218	    )) {
219		mtx_unlock(&enc_mtx);
220		return (0);
221	}
222
223	i = min((*mp)->m_pkthdr.len, max_protohdr);
224	if ((*mp)->m_len < i) {
225		*mp = m_pullup(*mp, i);
226		if (*mp == NULL) {
227			printf("%s: m_pullup failed\n", __func__);
228			mtx_unlock(&enc_mtx);
229			return (-1);
230		}
231	}
232
233	error = 0;
234	ip = mtod(*mp, struct ip *);
235	switch (ip->ip_v) {
236		case 4:
237			/*
238			 * before calling the firewall, swap fields the same as
239			 * IP does. here we assume the header is contiguous
240			 */
241			ip->ip_len = ntohs(ip->ip_len);
242			ip->ip_off = ntohs(ip->ip_off);
243
244			error = pfil_run_hooks(&inet_pfil_hook, mp,
245			    encif, dir, NULL);
246
247			if (*mp == NULL || error != 0)
248				break;
249
250			/* restore byte ordering */
251			ip = mtod(*mp, struct ip *);
252			ip->ip_len = htons(ip->ip_len);
253			ip->ip_off = htons(ip->ip_off);
254			break;
255
256#ifdef INET6
257		case 6:
258			error = pfil_run_hooks(&inet6_pfil_hook, mp,
259			    encif, dir, NULL);
260			break;
261#endif
262		default:
263			printf("%s: unknown IP version\n", __func__);
264	}
265
266	mtx_unlock(&enc_mtx);
267	if (*mp == NULL)
268		return (error);
269	if (error != 0)
270		goto bad;
271
272	return (error);
273
274bad:
275	mtx_unlock(&enc_mtx);
276	m_freem(*mp);
277	*mp = NULL;
278	return (error);
279}
280
281void
282ipsec_bpf(struct mbuf *m, struct secasvar *sav, int af)
283{
284	int flags;
285	struct enchdr hdr;
286
287	KASSERT(sav != NULL, ("%s: sav is null", __func__));
288
289	mtx_lock(&enc_mtx);
290	if (encif == NULL || (encif->if_drv_flags & IFF_DRV_RUNNING) == 0) {
291		mtx_unlock(&enc_mtx);
292		return;
293	}
294
295	if (encif->if_bpf) {
296		flags = 0;
297		if (sav->alg_enc != SADB_EALG_NONE)
298			flags |= M_CONF;
299		if (sav->alg_auth != SADB_AALG_NONE)
300			flags |= M_AUTH;
301
302		/*
303		 * We need to prepend the address family as a four byte
304		 * field.  Cons up a dummy header to pacify bpf.  This
305		 * is safe because bpf will only read from the mbuf
306		 * (i.e., it won't try to free it or keep a pointer a
307		 * to it).
308		 */
309		hdr.af = af;
310		hdr.spi = sav->spi;
311		hdr.flags = flags;
312
313		bpf_mtap2(encif->if_bpf, &hdr, sizeof(hdr), m);
314	}
315	mtx_unlock(&enc_mtx);
316}
317