xref: /netbsd-src/sys/rump/net/lib/libvirtif/if_virt.c (revision 076e35792dd3cbda10d623a5f694d79c3a305d2c)
1*076e3579Sriastradh /*	$NetBSD: if_virt.c,v 1.59 2021/06/16 00:21:20 riastradh Exp $	*/
261e869beSpooka 
361e869beSpooka /*
46b612185Spooka  * Copyright (c) 2008, 2013 Antti Kantee.  All Rights Reserved.
561e869beSpooka  *
661e869beSpooka  * Redistribution and use in source and binary forms, with or without
761e869beSpooka  * modification, are permitted provided that the following conditions
861e869beSpooka  * are met:
961e869beSpooka  * 1. Redistributions of source code must retain the above copyright
1061e869beSpooka  *    notice, this list of conditions and the following disclaimer.
1161e869beSpooka  * 2. Redistributions in binary form must reproduce the above copyright
1261e869beSpooka  *    notice, this list of conditions and the following disclaimer in the
1361e869beSpooka  *    documentation and/or other materials provided with the distribution.
1461e869beSpooka  *
1561e869beSpooka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
1661e869beSpooka  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1761e869beSpooka  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1861e869beSpooka  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1961e869beSpooka  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2061e869beSpooka  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2161e869beSpooka  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2261e869beSpooka  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2361e869beSpooka  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2461e869beSpooka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2561e869beSpooka  * SUCH DAMAGE.
2661e869beSpooka  */
2761e869beSpooka 
28a768afd6Spooka #include <sys/cdefs.h>
29*076e3579Sriastradh __KERNEL_RCSID(0, "$NetBSD: if_virt.c,v 1.59 2021/06/16 00:21:20 riastradh Exp $");
30a768afd6Spooka 
3161e869beSpooka #include <sys/param.h>
321ff4490aSpooka #include <sys/kernel.h>
3361e869beSpooka #include <sys/kmem.h>
343afd44cfStls #include <sys/cprng.h>
35bfdd7f7dSpooka #include <sys/module.h>
3661e869beSpooka 
3729ca934bSpooka #include <net/bpf.h>
3861e869beSpooka #include <net/if.h>
39c40bbed4Spooka #include <net/if_dl.h>
4061e869beSpooka #include <net/if_ether.h>
4161e869beSpooka 
4261e869beSpooka #include <netinet/in.h>
4361e869beSpooka #include <netinet/in_var.h>
4461e869beSpooka 
45be1e0a38Spooka #include "if_virt.h"
46be1e2f2aSpooka #include "virtif_user.h"
4703390692Spooka 
4861e869beSpooka /*
496b612185Spooka  * Virtual interface.  Uses hypercalls to shovel packets back
506b612185Spooka  * and forth.  The exact method for shoveling depends on the
516b612185Spooka  * hypercall implementation.
5261e869beSpooka  */
5361e869beSpooka 
5461e869beSpooka static int	virtif_init(struct ifnet *);
5561e869beSpooka static int	virtif_ioctl(struct ifnet *, u_long, void *);
5661e869beSpooka static void	virtif_start(struct ifnet *);
5761e869beSpooka static void	virtif_stop(struct ifnet *, int);
5861e869beSpooka 
5961e869beSpooka struct virtif_sc {
60cc8bfe6cSpooka 	struct ethercom sc_ec;
6103390692Spooka 	struct virtif_user *sc_viu;
62c40bbed4Spooka 
63c40bbed4Spooka 	int sc_num;
64c40bbed4Spooka 	char *sc_linkstr;
6561e869beSpooka };
6661e869beSpooka 
67a500a55cSpooka static int  virtif_clone(struct if_clone *, int);
68a500a55cSpooka static int  virtif_unclone(struct ifnet *);
6961e869beSpooka 
70be1e0a38Spooka struct if_clone VIF_CLONER =
71be1e0a38Spooka     IF_CLONE_INITIALIZER(VIF_NAME, virtif_clone, virtif_unclone);
7261e869beSpooka 
73d45448faSpooka static int
virtif_create(struct ifnet * ifp)74c40bbed4Spooka virtif_create(struct ifnet *ifp)
75c40bbed4Spooka {
76c40bbed4Spooka 	uint8_t enaddr[ETHER_ADDR_LEN] = { 0xb2, 0x0a, 0x00, 0x0b, 0x0e, 0x01 };
77c40bbed4Spooka 	char enaddrstr[3*ETHER_ADDR_LEN];
78c40bbed4Spooka 	struct virtif_sc *sc = ifp->if_softc;
79c40bbed4Spooka 	int error;
80c40bbed4Spooka 
81c40bbed4Spooka 	if (sc->sc_viu)
82c40bbed4Spooka 		panic("%s: already created", ifp->if_xname);
83c40bbed4Spooka 
84c40bbed4Spooka 	enaddr[2] = cprng_fast32() & 0xff;
85c40bbed4Spooka 	enaddr[5] = sc->sc_num & 0xff;
86c40bbed4Spooka 
87c40bbed4Spooka 	if ((error = VIFHYPER_CREATE(sc->sc_linkstr,
88c40bbed4Spooka 	    sc, enaddr, &sc->sc_viu)) != 0) {
89c40bbed4Spooka 		printf("VIFHYPER_CREATE failed: %d\n", error);
90c40bbed4Spooka 		return error;
91c40bbed4Spooka 	}
92c40bbed4Spooka 
93c40bbed4Spooka 	ether_ifattach(ifp, enaddr);
94c40bbed4Spooka 	ether_snprintf(enaddrstr, sizeof(enaddrstr), enaddr);
95c40bbed4Spooka 	aprint_normal_ifnet(ifp, "Ethernet address %s\n", enaddrstr);
96c40bbed4Spooka 
97c40bbed4Spooka 	IFQ_SET_READY(&ifp->if_snd);
98c40bbed4Spooka 
99c40bbed4Spooka 	return 0;
100c40bbed4Spooka }
101c40bbed4Spooka 
102c40bbed4Spooka static int
virtif_clone(struct if_clone * ifc,int num)103d9c4c208Spooka virtif_clone(struct if_clone *ifc, int num)
10461e869beSpooka {
10561e869beSpooka 	struct virtif_sc *sc;
10661e869beSpooka 	struct ifnet *ifp;
10703390692Spooka 	int error = 0;
1081ff4490aSpooka 
10961e869beSpooka 	sc = kmem_zalloc(sizeof(*sc), KM_SLEEP);
110c40bbed4Spooka 	sc->sc_num = num;
111cc8bfe6cSpooka 	ifp = &sc->sc_ec.ec_if;
112271d5dd1Spooka 
113271d5dd1Spooka 	if_initname(ifp, VIF_NAME, num);
11461e869beSpooka 	ifp->if_softc = sc;
1151ff4490aSpooka 
11661e869beSpooka 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
11761e869beSpooka 	ifp->if_init = virtif_init;
11861e869beSpooka 	ifp->if_ioctl = virtif_ioctl;
11961e869beSpooka 	ifp->if_start = virtif_start;
12061e869beSpooka 	ifp->if_stop = virtif_stop;
121c40bbed4Spooka 	ifp->if_mtu = ETHERMTU;
122c40bbed4Spooka 	ifp->if_dlt = DLT_EN10MB;
12361e869beSpooka 
124*076e3579Sriastradh 	if_initialize(ifp);
1259c4cd063Sozaki-r 	if_register(ifp);
12661e869beSpooka 
127c40bbed4Spooka #ifndef RUMP_VIF_LINKSTR
128c40bbed4Spooka 	/*
129c40bbed4Spooka 	 * if the underlying interface does not expect linkstr, we can
130c40bbed4Spooka 	 * create everything now.  Otherwise, we need to wait for
131c40bbed4Spooka 	 * SIOCSLINKSTR.
132c40bbed4Spooka 	 */
133c40bbed4Spooka #define LINKSTRNUMLEN 16
134c40bbed4Spooka 	sc->sc_linkstr = kmem_alloc(LINKSTRNUMLEN, KM_SLEEP);
13535096a90Smsaitoh 	if (sc->sc_linkstr == NULL) {
13635096a90Smsaitoh 		error = ENOMEM;
137*076e3579Sriastradh 		goto fail;
13835096a90Smsaitoh 	}
139c40bbed4Spooka 	snprintf(sc->sc_linkstr, LINKSTRNUMLEN, "%d", sc->sc_num);
140c40bbed4Spooka 	error = virtif_create(ifp);
1411ff4490aSpooka 	if (error) {
142*076e3579Sriastradh fail:
143c40bbed4Spooka 		if_detach(ifp);
14435096a90Smsaitoh 		if (sc->sc_linkstr != NULL)
14535096a90Smsaitoh 			kmem_free(sc->sc_linkstr, LINKSTRNUMLEN);
1468afd0f4cSmsaitoh #undef LINKSTRNUMLEN
147c40bbed4Spooka 		kmem_free(sc, sizeof(*sc));
148c40bbed4Spooka 		ifp->if_softc = NULL;
1491ff4490aSpooka 	}
150c40bbed4Spooka #endif /* !RUMP_VIF_LINKSTR */
1511ff4490aSpooka 
1521ff4490aSpooka 	return error;
15361e869beSpooka }
15461e869beSpooka 
15561e869beSpooka static int
virtif_unclone(struct ifnet * ifp)156a500a55cSpooka virtif_unclone(struct ifnet *ifp)
157a500a55cSpooka {
1581ff4490aSpooka 	struct virtif_sc *sc = ifp->if_softc;
159c2849206Spooka 	int rv;
160a500a55cSpooka 
161c40bbed4Spooka 	if (ifp->if_flags & IFF_UP)
162c40bbed4Spooka 		return EBUSY;
1631ff4490aSpooka 
164c2849206Spooka 	if ((rv = VIFHYPER_DYING(sc->sc_viu)) != 0)
165c2849206Spooka 		return rv;
16603390692Spooka 
1671ff4490aSpooka 	virtif_stop(ifp, 1);
1681ff4490aSpooka 	if_down(ifp);
1691ff4490aSpooka 
170be1e0a38Spooka 	VIFHYPER_DESTROY(sc->sc_viu);
1711ff4490aSpooka 
1721ff4490aSpooka 	kmem_free(sc, sizeof(*sc));
1731ff4490aSpooka 
1741ff4490aSpooka 	ether_ifdetach(ifp);
1751ff4490aSpooka 	if_detach(ifp);
1761ff4490aSpooka 
1771ff4490aSpooka 	return 0;
178a500a55cSpooka }
179a500a55cSpooka 
180a500a55cSpooka static int
virtif_init(struct ifnet * ifp)18161e869beSpooka virtif_init(struct ifnet *ifp)
18261e869beSpooka {
1831ff4490aSpooka 	struct virtif_sc *sc = ifp->if_softc;
18461e869beSpooka 
185c40bbed4Spooka 	if (sc->sc_viu == NULL)
186c40bbed4Spooka 		return ENXIO;
187c40bbed4Spooka 
18861e869beSpooka 	ifp->if_flags |= IFF_RUNNING;
18961e869beSpooka 	return 0;
19061e869beSpooka }
19161e869beSpooka 
19261e869beSpooka static int
virtif_ioctl(struct ifnet * ifp,u_long cmd,void * data)19361e869beSpooka virtif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
19461e869beSpooka {
195c40bbed4Spooka 	struct virtif_sc *sc = ifp->if_softc;
196c40bbed4Spooka 	int rv;
19761e869beSpooka 
198c40bbed4Spooka 	switch (cmd) {
199c40bbed4Spooka #ifdef RUMP_VIF_LINKSTR
200c40bbed4Spooka 	struct ifdrv *ifd;
201c40bbed4Spooka 	size_t linkstrlen;
202c40bbed4Spooka 
203c40bbed4Spooka #ifndef RUMP_VIF_LINKSTRMAX
204c40bbed4Spooka #define RUMP_VIF_LINKSTRMAX 4096
205c40bbed4Spooka #endif
206c40bbed4Spooka 
207c40bbed4Spooka 	case SIOCGLINKSTR:
208c40bbed4Spooka 		ifd = data;
209c40bbed4Spooka 
210c40bbed4Spooka 		if (!sc->sc_linkstr) {
211c40bbed4Spooka 			rv = ENOENT;
212c40bbed4Spooka 			break;
213c40bbed4Spooka 		}
214c40bbed4Spooka 		linkstrlen = strlen(sc->sc_linkstr)+1;
215c40bbed4Spooka 
216c40bbed4Spooka 		if (ifd->ifd_cmd == IFLINKSTR_QUERYLEN) {
217c40bbed4Spooka 			ifd->ifd_len = linkstrlen;
218c40bbed4Spooka 			rv = 0;
219c40bbed4Spooka 			break;
220c40bbed4Spooka 		}
221c40bbed4Spooka 		if (ifd->ifd_cmd != 0) {
222c40bbed4Spooka 			rv = ENOTTY;
223c40bbed4Spooka 			break;
224c40bbed4Spooka 		}
225c40bbed4Spooka 
226c40bbed4Spooka 		rv = copyoutstr(sc->sc_linkstr,
227c40bbed4Spooka 		    ifd->ifd_data, MIN(ifd->ifd_len,linkstrlen), NULL);
228c40bbed4Spooka 		break;
229c40bbed4Spooka 	case SIOCSLINKSTR:
230c40bbed4Spooka 		if (ifp->if_flags & IFF_UP) {
231c40bbed4Spooka 			rv = EBUSY;
232c40bbed4Spooka 			break;
233c40bbed4Spooka 		}
234c40bbed4Spooka 
235c40bbed4Spooka 		ifd = data;
236c40bbed4Spooka 
237c40bbed4Spooka 		if (ifd->ifd_cmd == IFLINKSTR_UNSET) {
238c40bbed4Spooka 			panic("unset linkstr not implemented");
239c40bbed4Spooka 		} else if (ifd->ifd_cmd != 0) {
240c40bbed4Spooka 			rv = ENOTTY;
241c40bbed4Spooka 			break;
242c40bbed4Spooka 		} else if (sc->sc_linkstr) {
243c40bbed4Spooka 			rv = EBUSY;
244c40bbed4Spooka 			break;
245c40bbed4Spooka 		}
246c40bbed4Spooka 
247c40bbed4Spooka 		if (ifd->ifd_len > RUMP_VIF_LINKSTRMAX) {
248c40bbed4Spooka 			rv = E2BIG;
249c40bbed4Spooka 			break;
250c40bbed4Spooka 		} else if (ifd->ifd_len < 1) {
251c40bbed4Spooka 			rv = EINVAL;
252c40bbed4Spooka 			break;
253c40bbed4Spooka 		}
254c40bbed4Spooka 
255c40bbed4Spooka 
256c40bbed4Spooka 		sc->sc_linkstr = kmem_alloc(ifd->ifd_len, KM_SLEEP);
257c40bbed4Spooka 		rv = copyinstr(ifd->ifd_data, sc->sc_linkstr,
258c40bbed4Spooka 		    ifd->ifd_len, NULL);
259c40bbed4Spooka 		if (rv) {
260c40bbed4Spooka 			kmem_free(sc->sc_linkstr, ifd->ifd_len);
261c40bbed4Spooka 			break;
262c40bbed4Spooka 		}
263c40bbed4Spooka 
264c40bbed4Spooka 		rv = virtif_create(ifp);
265c40bbed4Spooka 		if (rv) {
266c40bbed4Spooka 			kmem_free(sc->sc_linkstr, ifd->ifd_len);
267c40bbed4Spooka 		}
268c40bbed4Spooka 		break;
269c40bbed4Spooka #endif /* RUMP_VIF_LINKSTR */
270c40bbed4Spooka 	default:
271c40bbed4Spooka 		if (!sc->sc_linkstr)
272c40bbed4Spooka 			rv = ENXIO;
273c40bbed4Spooka 		else
27461e869beSpooka 			rv = ether_ioctl(ifp, cmd, data);
2759c2d055fSpooka 		if (rv == ENETRESET)
2769c2d055fSpooka 			rv = 0;
277c40bbed4Spooka 		break;
278c40bbed4Spooka 	}
27961e869beSpooka 
28061e869beSpooka 	return rv;
28161e869beSpooka }
28261e869beSpooka 
283c40bbed4Spooka /*
284c40bbed4Spooka  * Output packets in-context until outgoing queue is empty.
285c40bbed4Spooka  * Leave responsibility of choosing whether or not to drop the
286c40bbed4Spooka  * kernel lock to VIPHYPER_SEND().
287c40bbed4Spooka  */
288c40bbed4Spooka #define LB_SH 32
28961e869beSpooka static void
virtif_start(struct ifnet * ifp)29061e869beSpooka virtif_start(struct ifnet *ifp)
29161e869beSpooka {
29261e869beSpooka 	struct virtif_sc *sc = ifp->if_softc;
29306dceb4dSpooka 	struct mbuf *m, *m0;
29403390692Spooka 	struct iovec io[LB_SH];
29503390692Spooka 	int i;
29606dceb4dSpooka 
297c40bbed4Spooka 	ifp->if_flags |= IFF_OACTIVE;
298c40bbed4Spooka 
299c40bbed4Spooka 	for (;;) {
3001ff4490aSpooka 		IF_DEQUEUE(&ifp->if_snd, m0);
3011ff4490aSpooka 		if (!m0) {
302c40bbed4Spooka 			break;
3031ff4490aSpooka 		}
30406dceb4dSpooka 
30506dceb4dSpooka 		m = m0;
30630baf09aSpooka 		for (i = 0; i < LB_SH && m; ) {
30730baf09aSpooka 			if (m->m_len) {
30806dceb4dSpooka 				io[i].iov_base = mtod(m, void *);
30906dceb4dSpooka 				io[i].iov_len = m->m_len;
31030baf09aSpooka 				i++;
31130baf09aSpooka 			}
31206dceb4dSpooka 			m = m->m_next;
31306dceb4dSpooka 		}
31430baf09aSpooka 		if (i == LB_SH && m)
31506dceb4dSpooka 			panic("lazy bum");
3163cd62456Smsaitoh 		bpf_mtap(ifp, m0, BPF_D_OUT);
31706dceb4dSpooka 
318be1e0a38Spooka 		VIFHYPER_SEND(sc->sc_viu, io, i);
3191ff4490aSpooka 
3201ff4490aSpooka 		m_freem(m0);
321c85e2f36Sthorpej 		if_statinc(ifp, if_opackets);
3221ff4490aSpooka 	}
3231ff4490aSpooka 
324c40bbed4Spooka 	ifp->if_flags &= ~IFF_OACTIVE;
325c40bbed4Spooka }
3261ff4490aSpooka 
327c40bbed4Spooka static void
virtif_stop(struct ifnet * ifp,int disable)328c40bbed4Spooka virtif_stop(struct ifnet *ifp, int disable)
329c40bbed4Spooka {
330c40bbed4Spooka 
331c40bbed4Spooka 	/* XXX: VIFHYPER_STOP() */
332c40bbed4Spooka 
333c40bbed4Spooka 	ifp->if_flags &= ~IFF_RUNNING;
334c40bbed4Spooka }
335c40bbed4Spooka 
336c40bbed4Spooka void
VIF_DELIVERPKT(struct virtif_sc * sc,struct iovec * iov,size_t iovlen)337c40bbed4Spooka VIF_DELIVERPKT(struct virtif_sc *sc, struct iovec *iov, size_t iovlen)
338c40bbed4Spooka {
339c40bbed4Spooka 	struct ifnet *ifp = &sc->sc_ec.ec_if;
340c40bbed4Spooka 	struct ether_header *eth;
341c40bbed4Spooka 	struct mbuf *m;
342c40bbed4Spooka 	size_t i;
343c40bbed4Spooka 	int off, olen;
344c40bbed4Spooka 	bool passup;
345c40bbed4Spooka 	const int align
346c40bbed4Spooka 	    = ALIGN(sizeof(struct ether_header)) - sizeof(struct ether_header);
347c40bbed4Spooka 
348c40bbed4Spooka 	if ((ifp->if_flags & IFF_RUNNING) == 0)
349c40bbed4Spooka 		return;
350c40bbed4Spooka 
351c40bbed4Spooka 	m = m_gethdr(M_NOWAIT, MT_DATA);
352c40bbed4Spooka 	if (m == NULL)
353c40bbed4Spooka 		return; /* drop packet */
354c40bbed4Spooka 	m->m_len = m->m_pkthdr.len = 0;
355c40bbed4Spooka 
356c40bbed4Spooka 	for (i = 0, off = align; i < iovlen; i++) {
357c40bbed4Spooka 		olen = m->m_pkthdr.len;
358c40bbed4Spooka 		m_copyback(m, off, iov[i].iov_len, iov[i].iov_base);
359c40bbed4Spooka 		off += iov[i].iov_len;
360c40bbed4Spooka 		if (olen + off != m->m_pkthdr.len) {
361c40bbed4Spooka 			aprint_verbose_ifnet(ifp, "m_copyback failed\n");
362c40bbed4Spooka 			m_freem(m);
363c40bbed4Spooka 			return;
364c40bbed4Spooka 		}
365c40bbed4Spooka 	}
366c40bbed4Spooka 	m->m_data += align;
367c310bd10Spooka 	m->m_pkthdr.len -= align;
368c310bd10Spooka 	m->m_len -= align;
369c310bd10Spooka 
370c40bbed4Spooka 	eth = mtod(m, struct ether_header *);
371c40bbed4Spooka 	if (memcmp(eth->ether_dhost, CLLADDR(ifp->if_sadl),
372c40bbed4Spooka 	    ETHER_ADDR_LEN) == 0) {
373c40bbed4Spooka 		passup = true;
374c40bbed4Spooka 	} else if (ETHER_IS_MULTICAST(eth->ether_dhost)) {
375c40bbed4Spooka 		passup = true;
376c40bbed4Spooka 	} else if (ifp->if_flags & IFF_PROMISC) {
377c40bbed4Spooka 		m->m_flags |= M_PROMISC;
378c40bbed4Spooka 		passup = true;
379c40bbed4Spooka 	} else {
380c40bbed4Spooka 		passup = false;
381c40bbed4Spooka 	}
382c40bbed4Spooka 
383c40bbed4Spooka 	if (passup) {
384e1135cd9Sozaki-r 		int bound;
385d938d837Sozaki-r 		m_set_rcvif(m, ifp);
386c40bbed4Spooka 		KERNEL_LOCK(1, NULL);
387220ff4e7Sozaki-r 		/* Prevent LWP migrations between CPUs for psref(9) */
388e1135cd9Sozaki-r 		bound = curlwp_bind();
3899c4cd063Sozaki-r 		if_input(ifp, m);
390e1135cd9Sozaki-r 		curlwp_bindx(bound);
391c40bbed4Spooka 		KERNEL_UNLOCK_LAST(NULL);
392c40bbed4Spooka 	} else {
393c40bbed4Spooka 		m_freem(m);
394c40bbed4Spooka 	}
395c40bbed4Spooka 	m = NULL;
39606dceb4dSpooka }
397bfdd7f7dSpooka 
398b4d5024bSpooka /*
399b4d5024bSpooka  * The following ensures that no two modules using if_virt end up with
400b4d5024bSpooka  * the same module name.  MODULE() and modcmd wrapped in ... bad mojo.
401b4d5024bSpooka  */
402b4d5024bSpooka #define VIF_MOJO(x) MODULE(MODULE_CLASS_DRIVER,x,NULL);
403b4d5024bSpooka #define VIF_MODULE() VIF_MOJO(VIF_BASENAME(if_virt_,VIRTIF_BASE))
404b4d5024bSpooka #define VIF_MODCMD VIF_BASENAME3(if_virt_,VIRTIF_BASE,_modcmd)
405b4d5024bSpooka VIF_MODULE();
406bfdd7f7dSpooka static int
VIF_MODCMD(modcmd_t cmd,void * opaque)407b4d5024bSpooka VIF_MODCMD(modcmd_t cmd, void *opaque)
408bfdd7f7dSpooka {
409bfdd7f7dSpooka 	int error = 0;
410bfdd7f7dSpooka 
411bfdd7f7dSpooka 	switch (cmd) {
412bfdd7f7dSpooka 	case MODULE_CMD_INIT:
413bfdd7f7dSpooka 		if_clone_attach(&VIF_CLONER);
414bfdd7f7dSpooka 		break;
415bfdd7f7dSpooka 	case MODULE_CMD_FINI:
416bfdd7f7dSpooka 		/*
417bfdd7f7dSpooka 		 * not sure if interfaces are refcounted
418bfdd7f7dSpooka 		 * and properly protected
419bfdd7f7dSpooka 		 */
420bfdd7f7dSpooka #if 0
421bfdd7f7dSpooka 		if_clone_detach(&VIF_CLONER);
422bfdd7f7dSpooka #else
423bfdd7f7dSpooka 		error = ENOTTY;
424bfdd7f7dSpooka #endif
425bfdd7f7dSpooka 		break;
426bfdd7f7dSpooka 	default:
427bfdd7f7dSpooka 		error = ENOTTY;
428bfdd7f7dSpooka 	}
429bfdd7f7dSpooka 	return error;
430bfdd7f7dSpooka }
431