xref: /freebsd/sys/net/if_enc.c (revision 282a3889ebf826db9839be296ff1dd903f6d6d6e)
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$
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 
72 struct enchdr {
73 	u_int32_t af;
74 	u_int32_t spi;
75 	u_int32_t flags;
76 };
77 
78 static struct ifnet	*encif;
79 static struct mtx	enc_mtx;
80 
81 struct enc_softc {
82 	struct	ifnet *sc_ifp;
83 };
84 
85 static int	enc_ioctl(struct ifnet *, u_long, caddr_t);
86 static int	enc_output(struct ifnet *ifp, struct mbuf *m,
87 		    struct sockaddr *dst, struct rtentry *rt);
88 static int	enc_clone_create(struct if_clone *, int, caddr_t);
89 static void	enc_clone_destroy(struct ifnet *);
90 
91 IFC_SIMPLE_DECLARE(enc, 1);
92 
93 static void
94 enc_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 
103 static int
104 enc_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 
134 static int
135 enc_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 
151 static moduledata_t enc_mod = {
152 	"enc",
153 	enc_modevent,
154 	0
155 };
156 
157 DECLARE_MODULE(enc, enc_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
158 
159 static int
160 enc_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 */
171 static int
172 enc_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 
196 int
197 ipsec_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 
265 bad:
266 	m_freem(*mp);
267 	*mp = NULL;
268 	return (error);
269 }
270 
271 void
272 ipsec_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 (bpf_peers_present(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