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