xref: /openbsd-src/sys/net/if_rport.c (revision 45a062b0c5c65aea70077c7265851c79f0d7fa49)
1 /*	$OpenBSD: if_rport.c,v 1.1 2024/08/31 04:17:14 dlg Exp $ */
2 
3 /*
4  * Copyright (c) 2023 David Gwynne <dlg@openbsd.org>
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 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/mbuf.h>
22 #include <sys/socket.h>
23 #include <sys/sockio.h>
24 #include <sys/syslog.h>
25 #include <sys/queue.h>
26 
27 #include <net/if.h>
28 #include <net/if_var.h>
29 #include <net/if_types.h>
30 #include <net/route.h>
31 
32 #include <netinet/in.h>
33 #include <netinet/in_var.h>
34 #include <netinet/ip.h>
35 #include <netinet/ip_var.h>
36 #include <netinet/ip_ipip.h>
37 #include <netinet/ip_ecn.h>
38 
39 #ifdef INET6
40 #include <netinet6/in6_var.h>
41 #include <netinet/ip6.h>
42 #include <netinet6/ip6_var.h>
43 #endif /* INET6 */
44 
45 #include "bpfilter.h"
46 #if NBPFILTER > 0
47 #include <net/bpf.h>
48 #endif
49 
50 #ifdef MPLS
51 #include <netmpls/mpls.h>
52 #endif
53 
54 #include "pf.h"
55 #if NPF > 0
56 #include <net/pfvar.h>
57 #endif
58 
59 #define RPORT_MTU_MIN		1280
60 #define RPORT_MTU_MAX		32768 /* LOMTU, but could be higher */
61 #define RPORT_MTU_DEFAULT	RPORT_MTU_MAX
62 
63 struct rport_softc {
64 	struct ifnet			 sc_if;
65 
66 	unsigned int			 sc_peer_idx;
67 };
68 
69 static int	rport_clone_create(struct if_clone *, int);
70 static int	rport_clone_destroy(struct ifnet *);
71 
72 static int	rport_ioctl(struct ifnet *, u_long, caddr_t);
73 static int	rport_output(struct ifnet *, struct mbuf *, struct sockaddr *,
74 		    struct rtentry *);
75 static int	rport_enqueue(struct ifnet *, struct mbuf *);
76 static void	rport_start(struct ifqueue *);
77 static void	rport_input(struct ifnet *, struct mbuf *);
78 
79 static int	rport_up(struct rport_softc *);
80 static int	rport_down(struct rport_softc *);
81 
82 static int	rport_set_parent(struct rport_softc *,
83 		    const struct if_parent *);
84 static int	rport_get_parent(struct rport_softc *, struct if_parent *);
85 static int	rport_del_parent(struct rport_softc *);
86 
87 static struct if_clone rport_cloner =
88     IF_CLONE_INITIALIZER("rport", rport_clone_create, rport_clone_destroy);
89 
90 static struct rwlock rport_interfaces_lock =
91     RWLOCK_INITIALIZER("rports");
92 
93 void
94 rportattach(int count)
95 {
96 	if_clone_attach(&rport_cloner);
97 }
98 
99 static int
100 rport_clone_create(struct if_clone *ifc, int unit)
101 {
102 	struct rport_softc *sc;
103 	struct ifnet *ifp;
104 
105 	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
106 	ifp = &sc->sc_if;
107 
108 	snprintf(ifp->if_xname, sizeof(ifp->if_xname),
109 	    "%s%d", ifc->ifc_name, unit);
110 
111 	ifp->if_mtu = RPORT_MTU_DEFAULT;
112 	ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
113 	ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE;
114 	ifp->if_ioctl = rport_ioctl;
115 	ifp->if_bpf_mtap = p2p_bpf_mtap;
116 	ifp->if_output = rport_output;
117 	ifp->if_enqueue = rport_enqueue;
118 	ifp->if_qstart = rport_start;
119 	ifp->if_input = rport_input;
120 	ifp->if_rtrequest = p2p_rtrequest;
121 	ifp->if_type = IFT_TUNNEL;
122 	ifp->if_softc = sc;
123 
124 	if_attach(ifp);
125 	if_alloc_sadl(ifp);
126 	if_counters_alloc(ifp);
127 
128 #if NBPFILTER > 0
129 	bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t));
130 #endif
131 
132 	return (0);
133 }
134 
135 int
136 rport_clone_destroy(struct ifnet *ifp)
137 {
138 	struct rport_softc *sc = ifp->if_softc;
139 
140 	NET_LOCK();
141 	if (ISSET(ifp->if_flags, IFF_RUNNING))
142 		rport_down(sc);
143 	rport_del_parent(sc);
144 	NET_UNLOCK();
145 
146 	if_detach(ifp);
147 
148 	free(sc, M_DEVBUF, sizeof(*sc));
149 
150 	return (0);
151 }
152 
153 static int
154 rport_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
155     struct rtentry *rt)
156 {
157 	struct m_tag *mtag;
158 	int error = 0;
159 
160 	if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
161 		error = ENETDOWN;
162 		goto drop;
163 	}
164 
165 	switch (dst->sa_family) {
166 	case AF_INET:
167 #ifdef INET6
168 	case AF_INET6:
169 #endif
170 #ifdef MPLS
171 	case AF_MPLS:
172 #endif
173 		break;
174 	default:
175 		error = EAFNOSUPPORT;
176 		goto drop;
177 	}
178 
179 	/* Try to limit infinite recursion through misconfiguration. */
180 	mtag = NULL;
181 	while ((mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) != NULL) {
182 		if (*(int *)(mtag + 1) == ifp->if_index) {
183 			error = EIO;
184 			goto drop;
185 		}
186 	}
187 
188 	mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT);
189 	if (mtag == NULL) {
190 		error = ENOBUFS;
191 		goto drop;
192 	}
193 	*(int *)(mtag + 1) = ifp->if_index;
194 	m_tag_prepend(m, mtag);
195 
196 	m->m_flags &= ~(M_BCAST|M_MCAST);
197 	m->m_pkthdr.ph_family = dst->sa_family;
198 #if NPF > 0
199 	pf_pkt_addr_changed(m);
200 #endif
201 
202 	error = if_enqueue(ifp, m);
203 	if (error)
204 		counters_inc(ifp->if_counters, ifc_oerrors);
205 
206 	return (error);
207 
208 drop:
209 	m_freem(m);
210 	return (error);
211 }
212 
213 static int
214 rport_enqueue(struct ifnet *ifp, struct mbuf *m)
215 {
216 	struct ifqueue *ifq = &ifp->if_snd;
217 	int error;
218 
219 	error = ifq_enqueue(ifq, m);
220 	if (error)
221 		return (error);
222 
223 	/*
224 	 * always defer handover of packets to the peer to the ifq
225 	 * bundle task to provide control over the NET_LOCK scope.
226 	 */
227 	task_add(ifq->ifq_softnet, &ifq->ifq_bundle);
228 
229 	return (0);
230 }
231 
232 static void
233 rport_start(struct ifqueue *ifq)
234 {
235 	struct ifnet *ifp = ifq->ifq_if;
236 	struct rport_softc *sc = ifp->if_softc;
237 	struct ifnet *ifp0;
238 	struct mbuf *m;
239 
240 	ifp0 = if_get(sc->sc_peer_idx);
241 	if (ifp0 == NULL || !ISSET(ifp0->if_flags, IFF_RUNNING)) {
242 		ifq_purge(ifq);
243 		if_put(ifp0);
244 		return;
245 	}
246 
247 	NET_LOCK_SHARED();
248 	while ((m = ifq_dequeue(ifq)) != NULL) {
249 #if NBPFILTER > 0
250 		caddr_t if_bpf = READ_ONCE(ifp->if_bpf);
251 		if (if_bpf && bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family,
252 		    m, BPF_DIRECTION_OUT)) {
253 			m_freem(m);
254 			continue;
255 		}
256 #endif
257 
258 		if_vinput(ifp0, m);
259 	}
260 	NET_UNLOCK_SHARED();
261 
262 	if_put(ifp0);
263 }
264 
265 static void
266 rport_input(struct ifnet *ifp, struct mbuf *m)
267 {
268         switch (m->m_pkthdr.ph_family) {
269         case AF_INET:
270                 ipv4_input(ifp, m);
271                 break;
272 #ifdef INET6
273         case AF_INET6:
274                 ipv6_input(ifp, m);
275                 break;
276 #endif
277 #ifdef MPLS
278         case AF_MPLS:
279                 mpls_input(ifp, m);
280                 break;
281 #endif
282         default:
283 		counters_inc(ifp->if_counters, ifc_noproto);
284                 m_freem(m);
285                 break;
286         }
287 }
288 
289 static int
290 rport_up(struct rport_softc *sc)
291 {
292 	NET_ASSERT_LOCKED();
293 
294 	SET(sc->sc_if.if_flags, IFF_RUNNING);
295 
296 	return (0);
297 }
298 
299 static int
300 rport_down(struct rport_softc *sc)
301 {
302 	NET_ASSERT_LOCKED();
303 
304 	CLR(sc->sc_if.if_flags, IFF_RUNNING);
305 
306 	return (0);
307 }
308 
309 static int
310 rport_set_parent(struct rport_softc *sc, const struct if_parent *p)
311 {
312 	struct ifnet *ifp = &sc->sc_if;
313 	struct ifnet *ifp0;
314 	struct rport_softc *sc0;
315 	int error;
316 
317 	error = rw_enter(&rport_interfaces_lock, RW_WRITE | RW_INTR);
318 	if (error != 0)
319 		return (error);
320 
321 	ifp0 = if_unit(p->ifp_parent);
322 	if (ifp0 == NULL) {
323 		error = EINVAL;
324 		goto leave;
325 	}
326 
327 	if (ifp0 == ifp) {
328 		error = EINVAL;
329 		goto leave;
330 	}
331 
332 	if (ifp0->if_input != rport_input) {
333 		error = EPROTONOSUPPORT;
334 		goto put;
335 	}
336 
337 	sc0 = ifp0->if_softc;
338 
339 	if (sc->sc_peer_idx == ifp0->if_index) {
340 		/* nop */
341 		KASSERT(sc0->sc_peer_idx == ifp->if_index);
342 		goto put;
343 	}
344 
345 	if (sc->sc_peer_idx != 0 || sc0->sc_peer_idx != 0) {
346 		error = EBUSY;
347 		goto put;
348 	}
349 
350 	/* commit */
351 	sc->sc_peer_idx = ifp0->if_index;
352 	sc0->sc_peer_idx = ifp->if_index;
353 
354 put:
355 	if_put(ifp0);
356 leave:
357 	rw_exit(&rport_interfaces_lock);
358 
359 	return (error);
360 }
361 
362 static int
363 rport_get_parent(struct rport_softc *sc, struct if_parent *p)
364 {
365 	struct ifnet *ifp0;
366 	int error = 0;
367 
368 	ifp0 = if_get(sc->sc_peer_idx);
369 	if (ifp0 == NULL)
370 		error = EADDRNOTAVAIL;
371 	else {
372 		if (strlcpy(p->ifp_parent, ifp0->if_xname,
373 		    sizeof(p->ifp_parent)) >= sizeof(p->ifp_parent))
374 			panic("%s strlcpy", __func__);
375 	}
376 	if_put(ifp0);
377 
378 	return (error);
379 }
380 
381 static int
382 rport_del_parent(struct rport_softc *sc)
383 {
384 	struct rport_softc *sc0;
385 	struct ifnet *ifp0;
386 	int error;
387 
388 	error = rw_enter(&rport_interfaces_lock, RW_WRITE | RW_INTR);
389 	if (error != 0)
390 		return (error);
391 
392 	ifp0 = if_get(sc->sc_peer_idx);
393 	sc->sc_peer_idx = 0;
394 
395 	if (ifp0 != NULL) {
396 		sc0 = ifp0->if_softc;
397 		sc0->sc_peer_idx = 0;
398 	}
399 	if_put(ifp0);
400 
401 	rw_exit(&rport_interfaces_lock);
402 
403 	return (0);
404 }
405 
406 static int
407 rport_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
408 {
409 	struct rport_softc *sc = ifp->if_softc;
410 	struct ifreq *ifr = (struct ifreq *)data;
411 	int error = 0;
412 
413 	switch (cmd) {
414 	case SIOCSIFADDR:
415 		break;
416 	case SIOCSIFFLAGS:
417 		if (ISSET(ifp->if_flags, IFF_UP)) {
418 			if (!ISSET(ifp->if_flags, IFF_RUNNING))
419 				error = rport_up(sc);
420 		} else {
421 			if (ISSET(ifp->if_flags, IFF_RUNNING))
422 				error = rport_down(sc);
423 		}
424 		break;
425 
426 	case SIOCADDMULTI:
427 	case SIOCDELMULTI:
428 		break;
429 
430 	case SIOCSIFMTU:
431 		if (ifr->ifr_mtu < RPORT_MTU_MIN ||
432 		    ifr->ifr_mtu > RPORT_MTU_MAX) {
433 			error = EINVAL;
434 			break;
435 		}
436 
437 		ifp->if_mtu = ifr->ifr_mtu;
438 		break;
439 
440 	case SIOCSIFPARENT:
441 		error = rport_set_parent(sc, (struct if_parent *)data);
442 		break;
443 	case SIOCGIFPARENT:
444 		error = rport_get_parent(sc, (struct if_parent *)data);
445 		break;
446 	case SIOCDIFPARENT:
447 		error = rport_del_parent(sc);
448 		break;
449 
450 	default:
451 		error = ENOTTY;
452 		break;
453 	}
454 
455 	return (error);
456 }
457