xref: /openbsd/sys/net/if_sec.c (revision 5663f157)
1 /*	$OpenBSD: if_sec.c,v 1.11 2024/03/19 03:49:11 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
secattach(int n)115 secattach(int n)
116 {
117 	sec_mix = arc4random();
118 	if_clone_attach(&sec_cloner);
119 }
120 
121 static int
sec_clone_create(struct if_clone * ifc,int unit)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
sec_clone_destroy(struct ifnet * ifp)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
sec_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)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
sec_up(struct sec_softc * sc)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
sec_down(struct sec_softc * sc)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
sec_output(struct ifnet * ifp,struct mbuf * m,struct sockaddr * dst,struct rtentry * rt)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 	mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT);
319 	if (mtag == NULL) {
320 		error = ENOBUFS;
321 		goto drop;
322 	}
323 	*(int *)(mtag + 1) = ifp->if_index;
324 	m_tag_prepend(m, mtag);
325 
326 	m->m_pkthdr.ph_family = dst->sa_family;
327 
328 	error = if_enqueue(ifp, m);
329 	if (error != 0)
330 		counters_inc(ifp->if_counters, ifc_oqdrops);
331 
332 	return (error);
333 
334 drop:
335 	m_freem(m);
336 	return (error);
337 }
338 
339 static int
sec_enqueue(struct ifnet * ifp,struct mbuf * m)340 sec_enqueue(struct ifnet *ifp, struct mbuf *m)
341 {
342 	struct sec_softc *sc = ifp->if_softc;
343 	struct ifqueue *ifq = &ifp->if_snd;
344 	int error;
345 
346 	error = ifq_enqueue(ifq, m);
347 	if (error)
348 		return (error);
349 
350 	task_add(systq, &sc->sc_send);
351 
352 	return (0);
353 }
354 
355 static void
sec_send(void * arg)356 sec_send(void *arg)
357 {
358 	struct sec_softc *sc = arg;
359 	struct ifnet *ifp = &sc->sc_if;
360 	struct ifqueue *ifq = &ifp->if_snd;
361 	struct tdb *tdb;
362 	struct mbuf *m;
363 	int error;
364 	unsigned int flowid;
365 
366 	if (!ISSET(ifp->if_flags, IFF_RUNNING))
367 		return;
368 
369 	tdb = sec_tdb_get(sc->sc_unit);
370 	if (tdb == NULL)
371 		goto purge;
372 
373 	flowid = sc->sc_unit ^ sec_mix;
374 
375 	NET_LOCK();
376 	while ((m = ifq_dequeue(ifq)) != NULL) {
377 		CLR(m->m_flags, M_BCAST|M_MCAST);
378 
379 #if NPF > 0
380 		pf_pkt_addr_changed(m);
381 #endif
382 
383 #if NBPFILTER > 0
384 		if (ifp->if_bpf)
385 			bpf_mtap_af(ifp->if_bpf, m->m_pkthdr.ph_family, m,
386 			    BPF_DIRECTION_OUT);
387 #endif
388 
389 		m->m_pkthdr.pf.prio = sc->sc_txprio;
390 		SET(m->m_pkthdr.csum_flags, M_FLOWID);
391 		m->m_pkthdr.ph_flowid = flowid;
392 
393 		error = ipsp_process_packet(m, tdb,
394 		    m->m_pkthdr.ph_family, /* already tunnelled? */ 0);
395 		if (error != 0)
396 			counters_inc(ifp->if_counters, ifc_oerrors);
397 	}
398 	NET_UNLOCK();
399 
400 	tdb_unref(tdb);
401 	return;
402 
403 purge:
404 	counters_add(ifp->if_counters, ifc_oerrors, ifq_purge(ifq));
405 }
406 
407 static void
sec_start(struct ifqueue * ifq)408 sec_start(struct ifqueue *ifq)
409 {
410 	struct ifnet *ifp = ifq->ifq_if;
411 	struct sec_softc *sc = ifp->if_softc;
412 
413 	/* move this back to systq for KERNEL_LOCK */
414 	task_add(systq, &sc->sc_send);
415 }
416 
417 /*
418  * ipsec_input handling
419  */
420 
421 struct sec_softc *
sec_get(unsigned int unit)422 sec_get(unsigned int unit)
423 {
424 	unsigned int idx = stoeplitz_h32(unit) % nitems(sec_map);
425 	struct sec_bucket *sb = &sec_map[idx];
426 	struct sec_softc *sc;
427 
428 	smr_read_enter();
429 	SMR_SLIST_FOREACH(sc, sb, sc_entry) {
430 		if (sc->sc_unit == unit) {
431 			refcnt_take(&sc->sc_refs);
432 			break;
433 		}
434 	}
435 	smr_read_leave();
436 
437 	return (sc);
438 }
439 
440 void
sec_input(struct sec_softc * sc,int af,int proto,struct mbuf * m)441 sec_input(struct sec_softc *sc, int af, int proto, struct mbuf *m)
442 {
443 	struct ip *iph;
444 	int hlen;
445 
446 	switch (af) {
447 	case AF_INET:
448 		iph = mtod(m, struct ip *);
449 		hlen = iph->ip_hl << 2;
450 		break;
451 #ifdef INET6
452 	case AF_INET6:
453 		hlen = sizeof(struct ip6_hdr);
454 		break;
455 #endif
456 	default:
457 		unhandled_af(af);
458 	}
459 
460 	m_adj(m, hlen);
461 
462 	switch (proto) {
463 	case IPPROTO_IPV4:
464 		af = AF_INET;
465 		break;
466 	case IPPROTO_IPV6:
467 		af = AF_INET6;
468 		break;
469 	case IPPROTO_MPLS:
470 		af = AF_MPLS;
471 		break;
472 	default:
473 		af = AF_UNSPEC;
474 		break;
475 	}
476 
477 	m->m_pkthdr.ph_family = af;
478 
479 	if_vinput(&sc->sc_if, m);
480 }
481 
482 void
sec_put(struct sec_softc * sc)483 sec_put(struct sec_softc *sc)
484 {
485 	refcnt_rele_wake(&sc->sc_refs);
486 }
487 
488 /*
489  * tdb handling
490  */
491 
492 static int
sec_tdb_valid(struct tdb * tdb)493 sec_tdb_valid(struct tdb *tdb)
494 {
495 	KASSERT(ISSET(tdb->tdb_flags, TDBF_IFACE));
496 
497 	if (!ISSET(tdb->tdb_flags, TDBF_TUNNELING))
498 		return (0);
499 	if (ISSET(tdb->tdb_flags, TDBF_INVALID))
500 		return (0);
501 
502 	if (tdb->tdb_iface_dir != IPSP_DIRECTION_OUT)
503 		return (0);
504 
505 	return (1);
506 }
507 
508 /*
509  * these are called from netinet/ip_ipsp.c with tdb_sadb_mtx held,
510  * which we rely on to serialise modifications to the sec_tdbh.
511  */
512 
513 void
sec_tdb_insert(struct tdb * tdb)514 sec_tdb_insert(struct tdb *tdb)
515 {
516 	unsigned int idx;
517 	struct tdb **tdbp;
518 	struct tdb *ltdb;
519 
520 	if (!sec_tdb_valid(tdb))
521 		return;
522 
523 	idx = stoeplitz_h32(tdb->tdb_iface) % nitems(sec_tdbh);
524 	tdbp = &sec_tdbh[idx];
525 
526 	tdb_ref(tdb); /* take a ref for the SMR pointer */
527 
528 	/* wire the tdb into the head of the list */
529 	ltdb = SMR_PTR_GET_LOCKED(tdbp);
530 	SMR_PTR_SET_LOCKED(&tdb->tdb_dnext, ltdb);
531 	SMR_PTR_SET_LOCKED(tdbp, tdb);
532 }
533 
534 void
sec_tdb_remove(struct tdb * tdb)535 sec_tdb_remove(struct tdb *tdb)
536 {
537 	struct tdb **tdbp;
538 	struct tdb *ltdb;
539 	unsigned int idx;
540 
541 	if (!sec_tdb_valid(tdb))
542 		return;
543 
544 	idx = stoeplitz_h32(tdb->tdb_iface) % nitems(sec_tdbh);
545 	tdbp = &sec_tdbh[idx];
546 
547 	while ((ltdb = SMR_PTR_GET_LOCKED(tdbp)) != NULL) {
548 		if (ltdb == tdb) {
549 			/* take the tdb out of the list */
550 			ltdb = SMR_PTR_GET_LOCKED(&tdb->tdb_dnext);
551 			SMR_PTR_SET_LOCKED(tdbp, ltdb);
552 
553 			/* move the ref to the gc */
554 
555 			mtx_enter(&sec_tdb_gc_mtx);
556 			tdb->tdb_dnext = sec_tdb_gc_list;
557 			sec_tdb_gc_list = tdb;
558 			mtx_leave(&sec_tdb_gc_mtx);
559 			task_add(systq, &sec_tdb_gc_task);
560 
561 			return;
562 		}
563 
564 		tdbp = &ltdb->tdb_dnext;
565 	}
566 
567 	panic("%s: unable to find tdb %p", __func__, tdb);
568 }
569 
570 static void
sec_tdb_gc(void * null)571 sec_tdb_gc(void *null)
572 {
573 	struct tdb *tdb, *ntdb;
574 
575 	mtx_enter(&sec_tdb_gc_mtx);
576 	tdb = sec_tdb_gc_list;
577 	sec_tdb_gc_list = NULL;
578 	mtx_leave(&sec_tdb_gc_mtx);
579 
580 	if (tdb == NULL)
581 		return;
582 
583 	smr_barrier();
584 
585 	NET_LOCK();
586 	do {
587 		ntdb = tdb->tdb_dnext;
588 		tdb_unref(tdb);
589 		tdb = ntdb;
590 	} while (tdb != NULL);
591 	NET_UNLOCK();
592 }
593 
594 struct tdb *
sec_tdb_get(unsigned int unit)595 sec_tdb_get(unsigned int unit)
596 {
597 	unsigned int idx;
598 	struct tdb **tdbp;
599 	struct tdb *tdb;
600 
601 	idx = stoeplitz_h32(unit) % nitems(sec_map);
602 	tdbp = &sec_tdbh[idx];
603 
604 	smr_read_enter();
605 	while ((tdb = SMR_PTR_GET(tdbp)) != NULL) {
606 		KASSERT(ISSET(tdb->tdb_flags, TDBF_IFACE));
607 		if (!ISSET(tdb->tdb_flags, TDBF_DELETED) &&
608 		    tdb->tdb_iface == unit) {
609 			tdb_ref(tdb);
610 			break;
611 		}
612 
613 		tdbp = &tdb->tdb_dnext;
614 	}
615 	smr_read_leave();
616 
617 	return (tdb);
618 }
619