xref: /openbsd-src/sys/net/if_sec.c (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
1 /*	$OpenBSD: if_sec.c,v 1.7 2023/08/15 09:46:30 dlg Exp $ */
2 
3 /*
4  * Copyright (c) 2022 The University of Queensland
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * This code was written by David Gwynne <dlg@uq.edu.au> as part
21  * of the Information Technology Infrastructure Group (ITIG) in the
22  * Faculty of Engineering, Architecture and Information Technology
23  * (EAIT).
24  */
25 
26 #ifndef IPSEC
27 #error sec enabled without IPSEC defined
28 #endif
29 
30 #include "bpfilter.h"
31 #include "pf.h"
32 
33 #include <sys/param.h>
34 #include <sys/mbuf.h>
35 #include <sys/socket.h>
36 #include <sys/sockio.h>
37 #include <sys/systm.h>
38 #include <sys/errno.h>
39 #include <sys/smr.h>
40 #include <sys/refcnt.h>
41 #include <sys/task.h>
42 #include <sys/mutex.h>
43 
44 #include <net/if.h>
45 #include <net/if_var.h>
46 #include <net/if_types.h>
47 #include <net/toeplitz.h>
48 
49 #include <netinet/in.h>
50 #include <netinet/ip.h>
51 #include <netinet/ip_ipsp.h>
52 
53 #ifdef INET6
54 #include <netinet/ip6.h>
55 #endif
56 
57 #if NBPFILTER > 0
58 #include <net/bpf.h>
59 #endif
60 
61 #if NPF > 0
62 #include <net/pfvar.h>
63 #endif
64 
65 #define SEC_MTU		1280
66 #define SEC_MTU_MIN	1280
67 #define SEC_MTU_MAX	32768	/* could get closer to 64k... */
68 
69 struct sec_softc {
70 	struct ifnet			sc_if;
71 	unsigned int			sc_dead;
72 	unsigned int			sc_up;
73 
74 	struct task			sc_send;
75 	int				sc_txprio;
76 
77 	unsigned int			sc_unit;
78 	SMR_SLIST_ENTRY(sec_softc)	sc_entry;
79 	struct refcnt			sc_refs;
80 };
81 
82 SMR_SLIST_HEAD(sec_bucket, sec_softc);
83 
84 static int	sec_output(struct ifnet *, struct mbuf *, struct sockaddr *,
85 		    struct rtentry *);
86 static int	sec_enqueue(struct ifnet *, struct mbuf *);
87 static void	sec_send(void *);
88 static void	sec_start(struct ifqueue *);
89 
90 static int	sec_ioctl(struct ifnet *, u_long, caddr_t);
91 static int	sec_up(struct sec_softc *);
92 static int	sec_down(struct sec_softc *);
93 
94 static int	sec_clone_create(struct if_clone *, int);
95 static int	sec_clone_destroy(struct ifnet *);
96 
97 static struct tdb *
98 		sec_tdb_get(unsigned int);
99 static void	sec_tdb_gc(void *);
100 
101 static struct if_clone sec_cloner =
102     IF_CLONE_INITIALIZER("sec", sec_clone_create, sec_clone_destroy);
103 
104 static unsigned int		 sec_mix;
105 static struct sec_bucket	 sec_map[256] __aligned(CACHELINESIZE);
106 static struct tdb		*sec_tdbh[256] __aligned(CACHELINESIZE);
107 
108 static struct tdb		*sec_tdb_gc_list;
109 static struct task		 sec_tdb_gc_task =
110 				     TASK_INITIALIZER(sec_tdb_gc, NULL);
111 static struct mutex		 sec_tdb_gc_mtx =
112 				     MUTEX_INITIALIZER(IPL_MPFLOOR);
113 
114 void
115 secattach(int n)
116 {
117 	sec_mix = arc4random();
118 	if_clone_attach(&sec_cloner);
119 }
120 
121 static int
122 sec_clone_create(struct if_clone *ifc, int unit)
123 {
124 	struct sec_softc *sc;
125 	struct ifnet *ifp;
126 
127 	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
128 
129 	sc->sc_unit = unit;
130 
131 	task_set(&sc->sc_send, sec_send, sc);
132 
133 	snprintf(sc->sc_if.if_xname, sizeof sc->sc_if.if_xname, "%s%d",
134 	    ifc->ifc_name, unit);
135 
136 	ifp = &sc->sc_if;
137 	ifp->if_softc = sc;
138 	ifp->if_type = IFT_TUNNEL;
139 	ifp->if_mtu = SEC_MTU;
140 	ifp->if_flags = IFF_POINTOPOINT|IFF_MULTICAST;
141 	ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE;
142 	ifp->if_bpf_mtap = p2p_bpf_mtap;
143 	ifp->if_input = p2p_input;
144 	ifp->if_output = sec_output;
145 	ifp->if_enqueue = sec_enqueue;
146 	ifp->if_qstart = sec_start;
147 	ifp->if_ioctl = sec_ioctl;
148 	ifp->if_rtrequest = p2p_rtrequest;
149 
150 	if_counters_alloc(ifp);
151 	if_attach(ifp);
152 	if_alloc_sadl(ifp);
153 
154 #if NBPFILTER > 0
155 	bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t));
156 #endif
157 
158 	return (0);
159 }
160 
161 static int
162 sec_clone_destroy(struct ifnet *ifp)
163 {
164 	struct sec_softc *sc = ifp->if_softc;
165 
166 	NET_LOCK();
167 	sc->sc_dead = 1;
168 	if (ISSET(ifp->if_flags, IFF_RUNNING))
169 		sec_down(sc);
170 	NET_UNLOCK();
171 
172 	if_detach(ifp);
173 
174 	free(sc, M_DEVBUF, sizeof(*sc));
175 
176 	return (0);
177 }
178 
179 static int
180 sec_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
181 {
182 	struct sec_softc *sc = ifp->if_softc;
183 	struct ifreq *ifr = (struct ifreq *)data;
184 	int error = 0;
185 
186 	switch (cmd) {
187 	case SIOCSIFADDR:
188 		break;
189 
190 	case SIOCSIFFLAGS:
191 		if (ISSET(ifp->if_flags, IFF_UP)) {
192 			if (!ISSET(ifp->if_flags, IFF_RUNNING))
193 				error = sec_up(sc);
194 			else
195 				error = 0;
196 		} else {
197 			if (ISSET(ifp->if_flags, IFF_RUNNING))
198 				error = sec_down(sc);
199 		}
200 		break;
201 
202 	case SIOCADDMULTI:
203 	case SIOCDELMULTI:
204 		break;
205 
206 	case SIOCSIFMTU:
207 		if (ifr->ifr_mtu < SEC_MTU_MIN ||
208 		    ifr->ifr_mtu > SEC_MTU_MAX) {
209 			error = EINVAL;
210 			break;
211 		}
212 
213 		ifp->if_mtu = ifr->ifr_mtu;
214 		break;
215 
216 	default:
217 		error = ENOTTY;
218 		break;
219 	}
220 
221 	return (error);
222 }
223 
224 static int
225 sec_up(struct sec_softc *sc)
226 {
227 	struct ifnet *ifp = &sc->sc_if;
228 	unsigned int idx = stoeplitz_h32(sc->sc_unit) % nitems(sec_map);
229 
230 	NET_ASSERT_LOCKED();
231 	KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING));
232 
233 	if (sc->sc_dead)
234 		return (ENXIO);
235 
236 	/*
237 	 * coordinate with sec_down(). if sc_up is still up and
238 	 * we're here then something else is running sec_down.
239 	 */
240 	if (sc->sc_up)
241 		return (EBUSY);
242 
243 	sc->sc_up = 1;
244 
245 	refcnt_init(&sc->sc_refs);
246 	SET(ifp->if_flags, IFF_RUNNING);
247 	SMR_SLIST_INSERT_HEAD_LOCKED(&sec_map[idx], sc, sc_entry);
248 
249 	return (0);
250 }
251 
252 static int
253 sec_down(struct sec_softc *sc)
254 {
255 	struct ifnet *ifp = &sc->sc_if;
256 	unsigned int idx = stoeplitz_h32(sc->sc_unit) % nitems(sec_map);
257 
258 	NET_ASSERT_LOCKED();
259 	KASSERT(ISSET(ifp->if_flags, IFF_RUNNING));
260 
261 	/*
262 	 * taking sec down involves waiting for it to stop running
263 	 * in various contexts. this thread cannot hold netlock
264 	 * while waiting for a barrier for a task that could be trying
265 	 * to take netlock itself. so give up netlock, but don't clear
266 	 * sc_up to prevent sec_up from running.
267 	 */
268 
269 	CLR(ifp->if_flags, IFF_RUNNING);
270 	NET_UNLOCK();
271 
272 	smr_barrier();
273 	taskq_del_barrier(systq, &sc->sc_send);
274 
275 	refcnt_finalize(&sc->sc_refs, "secdown");
276 
277 	NET_LOCK();
278 	SMR_SLIST_REMOVE_LOCKED(&sec_map[idx], sc, sec_softc, sc_entry);
279 	sc->sc_up = 0;
280 
281 	return (0);
282 }
283 
284 static int
285 sec_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
286     struct rtentry *rt)
287 {
288 	struct m_tag *mtag;
289 	int error = 0;
290 
291 	if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
292 		error = ENETDOWN;
293 		goto drop;
294 	}
295 
296 	switch (dst->sa_family) {
297 	case AF_INET:
298 #ifdef INET6
299 	case AF_INET6:
300 #endif
301 #ifdef MPLS
302 	case AF_MPLS:
303 #endif
304 		break;
305 	default:
306 		error = EAFNOSUPPORT;
307 		goto drop;
308 	}
309 
310 	mtag = NULL;
311 	while ((mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) != NULL) {
312 		if (ifp->if_index == *(int *)(mtag + 1)) {
313 			error = EIO;
314 			goto drop;
315 		}
316 	}
317 
318 	m->m_pkthdr.ph_family = dst->sa_family;
319 
320 	error = if_enqueue(ifp, m);
321 	if (error != 0)
322 		counters_inc(ifp->if_counters, ifc_oerrors);
323 
324 	return (error);
325 
326 drop:
327 	m_freem(m);
328 	return (error);
329 }
330 
331 static int
332 sec_enqueue(struct ifnet *ifp, struct mbuf *m)
333 {
334 	struct sec_softc *sc = ifp->if_softc;
335 	struct ifqueue *ifq = &ifp->if_snd;
336 	int error;
337 
338 	error = ifq_enqueue(ifq, m);
339 	if (error)
340 		return (error);
341 
342 	task_add(systq, &sc->sc_send);
343 
344 	return (0);
345 }
346 
347 static void
348 sec_send(void *arg)
349 {
350 	struct sec_softc *sc = arg;
351 	struct ifnet *ifp = &sc->sc_if;
352 	struct ifqueue *ifq = &ifp->if_snd;
353 	struct tdb *tdb;
354 	struct mbuf *m;
355 	int error;
356 	unsigned int flowid;
357 
358 	if (!ISSET(ifp->if_flags, IFF_RUNNING))
359 		return;
360 
361 	tdb = sec_tdb_get(sc->sc_unit);
362 	if (tdb == NULL)
363 		goto purge;
364 
365 	flowid = sc->sc_unit ^ sec_mix;
366 
367 	NET_LOCK();
368 	while ((m = ifq_dequeue(ifq)) != NULL) {
369 		CLR(m->m_flags, M_BCAST|M_MCAST);
370 
371 #if NPF > 0
372 		pf_pkt_addr_changed(m);
373 #endif
374 
375 #if NBPFILTER > 0
376 		if (ifp->if_bpf)
377 			bpf_mtap_af(ifp->if_bpf, m->m_pkthdr.ph_family, m,
378 			    BPF_DIRECTION_OUT);
379 #endif
380 
381 		m->m_pkthdr.pf.prio = sc->sc_txprio;
382 		SET(m->m_pkthdr.csum_flags, M_FLOWID);
383 		m->m_pkthdr.ph_flowid = flowid;
384 
385 		error = ipsp_process_packet(m, tdb,
386 		    m->m_pkthdr.ph_family, /* already tunnelled? */ 0);
387 		if (error != 0)
388 			counters_inc(ifp->if_counters, ifc_oerrors);
389 	}
390 	NET_UNLOCK();
391 
392 	tdb_unref(tdb);
393 	return;
394 
395 purge:
396 	counters_add(ifp->if_counters, ifc_oerrors, ifq_purge(ifq));
397 }
398 
399 static void
400 sec_start(struct ifqueue *ifq)
401 {
402 	struct ifnet *ifp = ifq->ifq_if;
403 	struct sec_softc *sc = ifp->if_softc;
404 
405 	/* move this back to systq for KERNEL_LOCK */
406 	task_add(systq, &sc->sc_send);
407 }
408 
409 /*
410  * ipsec_input handling
411  */
412 
413 struct sec_softc *
414 sec_get(unsigned int unit)
415 {
416 	unsigned int idx = stoeplitz_h32(unit) % nitems(sec_map);
417 	struct sec_bucket *sb = &sec_map[idx];
418 	struct sec_softc *sc;
419 
420 	smr_read_enter();
421 	SMR_SLIST_FOREACH(sc, sb, sc_entry) {
422 		if (sc->sc_unit == unit) {
423 			refcnt_take(&sc->sc_refs);
424 			break;
425 		}
426 	}
427 	smr_read_leave();
428 
429 	return (sc);
430 }
431 
432 void
433 sec_input(struct sec_softc *sc, int af, int proto, struct mbuf *m)
434 {
435 	struct ip *iph;
436 	int hlen;
437 
438 	switch (af) {
439 	case AF_INET:
440 		iph = mtod(m, struct ip *);
441 		hlen = iph->ip_hl << 2;
442 		break;
443 #ifdef INET6
444 	case AF_INET6:
445 		hlen = sizeof(struct ip6_hdr);
446 		break;
447 #endif
448 	default:
449 		unhandled_af(af);
450 	}
451 
452 	m_adj(m, hlen);
453 
454 	switch (proto) {
455 	case IPPROTO_IPV4:
456 		af = AF_INET;
457 		break;
458 	case IPPROTO_IPV6:
459 		af = AF_INET6;
460 		break;
461 	case IPPROTO_MPLS:
462 		af = AF_MPLS;
463 		break;
464 	default:
465 		af = AF_UNSPEC;
466 		break;
467 	}
468 
469 	m->m_pkthdr.ph_family = af;
470 
471 	if_vinput(&sc->sc_if, m);
472 }
473 
474 void
475 sec_put(struct sec_softc *sc)
476 {
477 	refcnt_rele_wake(&sc->sc_refs);
478 }
479 
480 /*
481  * tdb handling
482  */
483 
484 static int
485 sec_tdb_valid(struct tdb *tdb)
486 {
487 	KASSERT(ISSET(tdb->tdb_flags, TDBF_IFACE));
488 
489 	if (!ISSET(tdb->tdb_flags, TDBF_TUNNELING))
490 		return (0);
491 	if (ISSET(tdb->tdb_flags, TDBF_INVALID))
492 		return (0);
493 
494 	if (tdb->tdb_iface_dir != IPSP_DIRECTION_OUT)
495 		return (0);
496 
497 	return (1);
498 }
499 
500 /*
501  * these are called from netinet/ip_ipsp.c with tdb_sadb_mtx held,
502  * which we rely on to serialise modifications to the sec_tdbh.
503  */
504 
505 void
506 sec_tdb_insert(struct tdb *tdb)
507 {
508 	unsigned int idx;
509 	struct tdb **tdbp;
510 	struct tdb *ltdb;
511 
512 	if (!sec_tdb_valid(tdb))
513 		return;
514 
515 	idx = stoeplitz_h32(tdb->tdb_iface) % nitems(sec_tdbh);
516 	tdbp = &sec_tdbh[idx];
517 
518 	tdb_ref(tdb); /* take a ref for the SMR pointer */
519 
520 	/* wire the tdb into the head of the list */
521 	ltdb = SMR_PTR_GET_LOCKED(tdbp);
522 	SMR_PTR_SET_LOCKED(&tdb->tdb_dnext, ltdb);
523 	SMR_PTR_SET_LOCKED(tdbp, tdb);
524 }
525 
526 void
527 sec_tdb_remove(struct tdb *tdb)
528 {
529 	struct tdb **tdbp;
530 	struct tdb *ltdb;
531 	unsigned int idx;
532 
533 	if (!sec_tdb_valid(tdb))
534 		return;
535 
536 	idx = stoeplitz_h32(tdb->tdb_iface) % nitems(sec_tdbh);
537 	tdbp = &sec_tdbh[idx];
538 
539 	while ((ltdb = SMR_PTR_GET_LOCKED(tdbp)) != NULL) {
540 		if (ltdb == tdb) {
541 			/* take the tdb out of the list */
542 			ltdb = SMR_PTR_GET_LOCKED(&tdb->tdb_dnext);
543 			SMR_PTR_SET_LOCKED(tdbp, ltdb);
544 
545 			/* move the ref to the gc */
546 
547 			mtx_enter(&sec_tdb_gc_mtx);
548 			tdb->tdb_dnext = sec_tdb_gc_list;
549 			sec_tdb_gc_list = tdb;
550 			mtx_leave(&sec_tdb_gc_mtx);
551 			task_add(systq, &sec_tdb_gc_task);
552 
553 			return;
554 		}
555 
556 		tdbp = &ltdb->tdb_dnext;
557 	}
558 
559 	panic("%s: unable to find tdb %p", __func__, tdb);
560 }
561 
562 static void
563 sec_tdb_gc(void *null)
564 {
565 	struct tdb *tdb, *ntdb;
566 
567 	mtx_enter(&sec_tdb_gc_mtx);
568 	tdb = sec_tdb_gc_list;
569 	sec_tdb_gc_list = NULL;
570 	mtx_leave(&sec_tdb_gc_mtx);
571 
572 	if (tdb == NULL)
573 		return;
574 
575 	smr_barrier();
576 
577 	NET_LOCK();
578 	do {
579 		ntdb = tdb->tdb_dnext;
580 		tdb_unref(tdb);
581 		tdb = ntdb;
582 	} while (tdb != NULL);
583 	NET_UNLOCK();
584 }
585 
586 struct tdb *
587 sec_tdb_get(unsigned int unit)
588 {
589 	unsigned int idx;
590 	struct tdb **tdbp;
591 	struct tdb *tdb;
592 
593 	idx = stoeplitz_h32(unit) % nitems(sec_map);
594 	tdbp = &sec_tdbh[idx];
595 
596 	smr_read_enter();
597 	while ((tdb = SMR_PTR_GET(tdbp)) != NULL) {
598 		KASSERT(ISSET(tdb->tdb_flags, TDBF_IFACE));
599 		if (!ISSET(tdb->tdb_flags, TDBF_DELETED) &&
600 		    tdb->tdb_iface == unit) {
601 			tdb_ref(tdb);
602 			break;
603 		}
604 
605 		tdbp = &tdb->tdb_dnext;
606 	}
607 	smr_read_leave();
608 
609 	return (tdb);
610 }
611