1/*	$NetBSD: if_virt.c,v 1.59 2021/06/16 00:21:20 riastradh Exp $	*/
2
3/*
4 * Copyright (c) 2008, 2013 Antti Kantee.  All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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 ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * 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 OR
21 * 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
28#include <sys/cdefs.h>
29__KERNEL_RCSID(0, "$NetBSD: if_virt.c,v 1.59 2021/06/16 00:21:20 riastradh Exp $");
30
31#include <sys/param.h>
32#include <sys/kernel.h>
33#include <sys/kmem.h>
34#include <sys/cprng.h>
35#include <sys/module.h>
36
37#include <net/bpf.h>
38#include <net/if.h>
39#include <net/if_dl.h>
40#include <net/if_ether.h>
41
42#include <netinet/in.h>
43#include <netinet/in_var.h>
44
45#include "if_virt.h"
46#include "virtif_user.h"
47
48/*
49 * Virtual interface.  Uses hypercalls to shovel packets back
50 * and forth.  The exact method for shoveling depends on the
51 * hypercall implementation.
52 */
53
54static int	virtif_init(struct ifnet *);
55static int	virtif_ioctl(struct ifnet *, u_long, void *);
56static void	virtif_start(struct ifnet *);
57static void	virtif_stop(struct ifnet *, int);
58
59struct virtif_sc {
60	struct ethercom sc_ec;
61	struct virtif_user *sc_viu;
62
63	int sc_num;
64	char *sc_linkstr;
65};
66
67static int  virtif_clone(struct if_clone *, int);
68static int  virtif_unclone(struct ifnet *);
69
70struct if_clone VIF_CLONER =
71    IF_CLONE_INITIALIZER(VIF_NAME, virtif_clone, virtif_unclone);
72
73static int
74virtif_create(struct ifnet *ifp)
75{
76	uint8_t enaddr[ETHER_ADDR_LEN] = { 0xb2, 0x0a, 0x00, 0x0b, 0x0e, 0x01 };
77	char enaddrstr[3*ETHER_ADDR_LEN];
78	struct virtif_sc *sc = ifp->if_softc;
79	int error;
80
81	if (sc->sc_viu)
82		panic("%s: already created", ifp->if_xname);
83
84	enaddr[2] = cprng_fast32() & 0xff;
85	enaddr[5] = sc->sc_num & 0xff;
86
87	if ((error = VIFHYPER_CREATE(sc->sc_linkstr,
88	    sc, enaddr, &sc->sc_viu)) != 0) {
89		printf("VIFHYPER_CREATE failed: %d\n", error);
90		return error;
91	}
92
93	ether_ifattach(ifp, enaddr);
94	ether_snprintf(enaddrstr, sizeof(enaddrstr), enaddr);
95	aprint_normal_ifnet(ifp, "Ethernet address %s\n", enaddrstr);
96
97	IFQ_SET_READY(&ifp->if_snd);
98
99	return 0;
100}
101
102static int
103virtif_clone(struct if_clone *ifc, int num)
104{
105	struct virtif_sc *sc;
106	struct ifnet *ifp;
107	int error = 0;
108
109	sc = kmem_zalloc(sizeof(*sc), KM_SLEEP);
110	sc->sc_num = num;
111	ifp = &sc->sc_ec.ec_if;
112
113	if_initname(ifp, VIF_NAME, num);
114	ifp->if_softc = sc;
115
116	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
117	ifp->if_init = virtif_init;
118	ifp->if_ioctl = virtif_ioctl;
119	ifp->if_start = virtif_start;
120	ifp->if_stop = virtif_stop;
121	ifp->if_mtu = ETHERMTU;
122	ifp->if_dlt = DLT_EN10MB;
123
124	if_initialize(ifp);
125	if_register(ifp);
126
127#ifndef RUMP_VIF_LINKSTR
128	/*
129	 * if the underlying interface does not expect linkstr, we can
130	 * create everything now.  Otherwise, we need to wait for
131	 * SIOCSLINKSTR.
132	 */
133#define LINKSTRNUMLEN 16
134	sc->sc_linkstr = kmem_alloc(LINKSTRNUMLEN, KM_SLEEP);
135	if (sc->sc_linkstr == NULL) {
136		error = ENOMEM;
137		goto fail;
138	}
139	snprintf(sc->sc_linkstr, LINKSTRNUMLEN, "%d", sc->sc_num);
140	error = virtif_create(ifp);
141	if (error) {
142fail:
143		if_detach(ifp);
144		if (sc->sc_linkstr != NULL)
145			kmem_free(sc->sc_linkstr, LINKSTRNUMLEN);
146#undef LINKSTRNUMLEN
147		kmem_free(sc, sizeof(*sc));
148		ifp->if_softc = NULL;
149	}
150#endif /* !RUMP_VIF_LINKSTR */
151
152	return error;
153}
154
155static int
156virtif_unclone(struct ifnet *ifp)
157{
158	struct virtif_sc *sc = ifp->if_softc;
159	int rv;
160
161	if (ifp->if_flags & IFF_UP)
162		return EBUSY;
163
164	if ((rv = VIFHYPER_DYING(sc->sc_viu)) != 0)
165		return rv;
166
167	virtif_stop(ifp, 1);
168	if_down(ifp);
169
170	VIFHYPER_DESTROY(sc->sc_viu);
171
172	kmem_free(sc, sizeof(*sc));
173
174	ether_ifdetach(ifp);
175	if_detach(ifp);
176
177	return 0;
178}
179
180static int
181virtif_init(struct ifnet *ifp)
182{
183	struct virtif_sc *sc = ifp->if_softc;
184
185	if (sc->sc_viu == NULL)
186		return ENXIO;
187
188	ifp->if_flags |= IFF_RUNNING;
189	return 0;
190}
191
192static int
193virtif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
194{
195	struct virtif_sc *sc = ifp->if_softc;
196	int rv;
197
198	switch (cmd) {
199#ifdef RUMP_VIF_LINKSTR
200	struct ifdrv *ifd;
201	size_t linkstrlen;
202
203#ifndef RUMP_VIF_LINKSTRMAX
204#define RUMP_VIF_LINKSTRMAX 4096
205#endif
206
207	case SIOCGLINKSTR:
208		ifd = data;
209
210		if (!sc->sc_linkstr) {
211			rv = ENOENT;
212			break;
213		}
214		linkstrlen = strlen(sc->sc_linkstr)+1;
215
216		if (ifd->ifd_cmd == IFLINKSTR_QUERYLEN) {
217			ifd->ifd_len = linkstrlen;
218			rv = 0;
219			break;
220		}
221		if (ifd->ifd_cmd != 0) {
222			rv = ENOTTY;
223			break;
224		}
225
226		rv = copyoutstr(sc->sc_linkstr,
227		    ifd->ifd_data, MIN(ifd->ifd_len,linkstrlen), NULL);
228		break;
229	case SIOCSLINKSTR:
230		if (ifp->if_flags & IFF_UP) {
231			rv = EBUSY;
232			break;
233		}
234
235		ifd = data;
236
237		if (ifd->ifd_cmd == IFLINKSTR_UNSET) {
238			panic("unset linkstr not implemented");
239		} else if (ifd->ifd_cmd != 0) {
240			rv = ENOTTY;
241			break;
242		} else if (sc->sc_linkstr) {
243			rv = EBUSY;
244			break;
245		}
246
247		if (ifd->ifd_len > RUMP_VIF_LINKSTRMAX) {
248			rv = E2BIG;
249			break;
250		} else if (ifd->ifd_len < 1) {
251			rv = EINVAL;
252			break;
253		}
254
255
256		sc->sc_linkstr = kmem_alloc(ifd->ifd_len, KM_SLEEP);
257		rv = copyinstr(ifd->ifd_data, sc->sc_linkstr,
258		    ifd->ifd_len, NULL);
259		if (rv) {
260			kmem_free(sc->sc_linkstr, ifd->ifd_len);
261			break;
262		}
263
264		rv = virtif_create(ifp);
265		if (rv) {
266			kmem_free(sc->sc_linkstr, ifd->ifd_len);
267		}
268		break;
269#endif /* RUMP_VIF_LINKSTR */
270	default:
271		if (!sc->sc_linkstr)
272			rv = ENXIO;
273		else
274			rv = ether_ioctl(ifp, cmd, data);
275		if (rv == ENETRESET)
276			rv = 0;
277		break;
278	}
279
280	return rv;
281}
282
283/*
284 * Output packets in-context until outgoing queue is empty.
285 * Leave responsibility of choosing whether or not to drop the
286 * kernel lock to VIPHYPER_SEND().
287 */
288#define LB_SH 32
289static void
290virtif_start(struct ifnet *ifp)
291{
292	struct virtif_sc *sc = ifp->if_softc;
293	struct mbuf *m, *m0;
294	struct iovec io[LB_SH];
295	int i;
296
297	ifp->if_flags |= IFF_OACTIVE;
298
299	for (;;) {
300		IF_DEQUEUE(&ifp->if_snd, m0);
301		if (!m0) {
302			break;
303		}
304
305		m = m0;
306		for (i = 0; i < LB_SH && m; ) {
307			if (m->m_len) {
308				io[i].iov_base = mtod(m, void *);
309				io[i].iov_len = m->m_len;
310				i++;
311			}
312			m = m->m_next;
313		}
314		if (i == LB_SH && m)
315			panic("lazy bum");
316		bpf_mtap(ifp, m0, BPF_D_OUT);
317
318		VIFHYPER_SEND(sc->sc_viu, io, i);
319
320		m_freem(m0);
321		if_statinc(ifp, if_opackets);
322	}
323
324	ifp->if_flags &= ~IFF_OACTIVE;
325}
326
327static void
328virtif_stop(struct ifnet *ifp, int disable)
329{
330
331	/* XXX: VIFHYPER_STOP() */
332
333	ifp->if_flags &= ~IFF_RUNNING;
334}
335
336void
337VIF_DELIVERPKT(struct virtif_sc *sc, struct iovec *iov, size_t iovlen)
338{
339	struct ifnet *ifp = &sc->sc_ec.ec_if;
340	struct ether_header *eth;
341	struct mbuf *m;
342	size_t i;
343	int off, olen;
344	bool passup;
345	const int align
346	    = ALIGN(sizeof(struct ether_header)) - sizeof(struct ether_header);
347
348	if ((ifp->if_flags & IFF_RUNNING) == 0)
349		return;
350
351	m = m_gethdr(M_NOWAIT, MT_DATA);
352	if (m == NULL)
353		return; /* drop packet */
354	m->m_len = m->m_pkthdr.len = 0;
355
356	for (i = 0, off = align; i < iovlen; i++) {
357		olen = m->m_pkthdr.len;
358		m_copyback(m, off, iov[i].iov_len, iov[i].iov_base);
359		off += iov[i].iov_len;
360		if (olen + off != m->m_pkthdr.len) {
361			aprint_verbose_ifnet(ifp, "m_copyback failed\n");
362			m_freem(m);
363			return;
364		}
365	}
366	m->m_data += align;
367	m->m_pkthdr.len -= align;
368	m->m_len -= align;
369
370	eth = mtod(m, struct ether_header *);
371	if (memcmp(eth->ether_dhost, CLLADDR(ifp->if_sadl),
372	    ETHER_ADDR_LEN) == 0) {
373		passup = true;
374	} else if (ETHER_IS_MULTICAST(eth->ether_dhost)) {
375		passup = true;
376	} else if (ifp->if_flags & IFF_PROMISC) {
377		m->m_flags |= M_PROMISC;
378		passup = true;
379	} else {
380		passup = false;
381	}
382
383	if (passup) {
384		int bound;
385		m_set_rcvif(m, ifp);
386		KERNEL_LOCK(1, NULL);
387		/* Prevent LWP migrations between CPUs for psref(9) */
388		bound = curlwp_bind();
389		if_input(ifp, m);
390		curlwp_bindx(bound);
391		KERNEL_UNLOCK_LAST(NULL);
392	} else {
393		m_freem(m);
394	}
395	m = NULL;
396}
397
398/*
399 * The following ensures that no two modules using if_virt end up with
400 * the same module name.  MODULE() and modcmd wrapped in ... bad mojo.
401 */
402#define VIF_MOJO(x) MODULE(MODULE_CLASS_DRIVER,x,NULL);
403#define VIF_MODULE() VIF_MOJO(VIF_BASENAME(if_virt_,VIRTIF_BASE))
404#define VIF_MODCMD VIF_BASENAME3(if_virt_,VIRTIF_BASE,_modcmd)
405VIF_MODULE();
406static int
407VIF_MODCMD(modcmd_t cmd, void *opaque)
408{
409	int error = 0;
410
411	switch (cmd) {
412	case MODULE_CMD_INIT:
413		if_clone_attach(&VIF_CLONER);
414		break;
415	case MODULE_CMD_FINI:
416		/*
417		 * not sure if interfaces are refcounted
418		 * and properly protected
419		 */
420#if 0
421		if_clone_detach(&VIF_CLONER);
422#else
423		error = ENOTTY;
424#endif
425		break;
426	default:
427		error = ENOTTY;
428	}
429	return error;
430}
431