/* $NetBSD: if_virt.c,v 1.57 2018/06/26 06:48:03 msaitoh Exp $ */ /* * Copyright (c) 2008, 2013 Antti Kantee. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD: if_virt.c,v 1.57 2018/06/26 06:48:03 msaitoh Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include "if_virt.h" #include "virtif_user.h" /* * Virtual interface. Uses hypercalls to shovel packets back * and forth. The exact method for shoveling depends on the * hypercall implementation. */ static int virtif_init(struct ifnet *); static int virtif_ioctl(struct ifnet *, u_long, void *); static void virtif_start(struct ifnet *); static void virtif_stop(struct ifnet *, int); struct virtif_sc { struct ethercom sc_ec; struct virtif_user *sc_viu; int sc_num; char *sc_linkstr; }; static int virtif_clone(struct if_clone *, int); static int virtif_unclone(struct ifnet *); struct if_clone VIF_CLONER = IF_CLONE_INITIALIZER(VIF_NAME, virtif_clone, virtif_unclone); static int virtif_create(struct ifnet *ifp) { uint8_t enaddr[ETHER_ADDR_LEN] = { 0xb2, 0x0a, 0x00, 0x0b, 0x0e, 0x01 }; char enaddrstr[3*ETHER_ADDR_LEN]; struct virtif_sc *sc = ifp->if_softc; int error; if (sc->sc_viu) panic("%s: already created", ifp->if_xname); enaddr[2] = cprng_fast32() & 0xff; enaddr[5] = sc->sc_num & 0xff; if ((error = VIFHYPER_CREATE(sc->sc_linkstr, sc, enaddr, &sc->sc_viu)) != 0) { printf("VIFHYPER_CREATE failed: %d\n", error); return error; } ether_ifattach(ifp, enaddr); ether_snprintf(enaddrstr, sizeof(enaddrstr), enaddr); aprint_normal_ifnet(ifp, "Ethernet address %s\n", enaddrstr); IFQ_SET_READY(&ifp->if_snd); return 0; } static int virtif_clone(struct if_clone *ifc, int num) { struct virtif_sc *sc; struct ifnet *ifp; int error = 0; sc = kmem_zalloc(sizeof(*sc), KM_SLEEP); sc->sc_num = num; ifp = &sc->sc_ec.ec_if; if_initname(ifp, VIF_NAME, num); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = virtif_init; ifp->if_ioctl = virtif_ioctl; ifp->if_start = virtif_start; ifp->if_stop = virtif_stop; ifp->if_mtu = ETHERMTU; ifp->if_dlt = DLT_EN10MB; error = if_initialize(ifp); if (error != 0) { aprint_error("%s: if_initialize failed(%d)\n", ifp->if_xname, error); goto fail_1; } if_register(ifp); #ifndef RUMP_VIF_LINKSTR /* * if the underlying interface does not expect linkstr, we can * create everything now. Otherwise, we need to wait for * SIOCSLINKSTR. */ #define LINKSTRNUMLEN 16 sc->sc_linkstr = kmem_alloc(LINKSTRNUMLEN, KM_SLEEP); if (sc->sc_linkstr == NULL) { error = ENOMEM; goto fail_2; } snprintf(sc->sc_linkstr, LINKSTRNUMLEN, "%d", sc->sc_num); error = virtif_create(ifp); if (error) { fail_2: if_detach(ifp); if (sc->sc_linkstr != NULL) kmem_free(sc->sc_linkstr, LINKSTRNUMLEN); #undef LINKSTRNUMLEN fail_1: kmem_free(sc, sizeof(*sc)); ifp->if_softc = NULL; } #endif /* !RUMP_VIF_LINKSTR */ return error; } static int virtif_unclone(struct ifnet *ifp) { struct virtif_sc *sc = ifp->if_softc; int rv; if (ifp->if_flags & IFF_UP) return EBUSY; if ((rv = VIFHYPER_DYING(sc->sc_viu)) != 0) return rv; virtif_stop(ifp, 1); if_down(ifp); VIFHYPER_DESTROY(sc->sc_viu); kmem_free(sc, sizeof(*sc)); ether_ifdetach(ifp); if_detach(ifp); return 0; } static int virtif_init(struct ifnet *ifp) { struct virtif_sc *sc = ifp->if_softc; if (sc->sc_viu == NULL) return ENXIO; ifp->if_flags |= IFF_RUNNING; return 0; } static int virtif_ioctl(struct ifnet *ifp, u_long cmd, void *data) { struct virtif_sc *sc = ifp->if_softc; int rv; switch (cmd) { #ifdef RUMP_VIF_LINKSTR struct ifdrv *ifd; size_t linkstrlen; #ifndef RUMP_VIF_LINKSTRMAX #define RUMP_VIF_LINKSTRMAX 4096 #endif case SIOCGLINKSTR: ifd = data; if (!sc->sc_linkstr) { rv = ENOENT; break; } linkstrlen = strlen(sc->sc_linkstr)+1; if (ifd->ifd_cmd == IFLINKSTR_QUERYLEN) { ifd->ifd_len = linkstrlen; rv = 0; break; } if (ifd->ifd_cmd != 0) { rv = ENOTTY; break; } rv = copyoutstr(sc->sc_linkstr, ifd->ifd_data, MIN(ifd->ifd_len,linkstrlen), NULL); break; case SIOCSLINKSTR: if (ifp->if_flags & IFF_UP) { rv = EBUSY; break; } ifd = data; if (ifd->ifd_cmd == IFLINKSTR_UNSET) { panic("unset linkstr not implemented"); } else if (ifd->ifd_cmd != 0) { rv = ENOTTY; break; } else if (sc->sc_linkstr) { rv = EBUSY; break; } if (ifd->ifd_len > RUMP_VIF_LINKSTRMAX) { rv = E2BIG; break; } else if (ifd->ifd_len < 1) { rv = EINVAL; break; } sc->sc_linkstr = kmem_alloc(ifd->ifd_len, KM_SLEEP); rv = copyinstr(ifd->ifd_data, sc->sc_linkstr, ifd->ifd_len, NULL); if (rv) { kmem_free(sc->sc_linkstr, ifd->ifd_len); break; } rv = virtif_create(ifp); if (rv) { kmem_free(sc->sc_linkstr, ifd->ifd_len); } break; #endif /* RUMP_VIF_LINKSTR */ default: if (!sc->sc_linkstr) rv = ENXIO; else rv = ether_ioctl(ifp, cmd, data); if (rv == ENETRESET) rv = 0; break; } return rv; } /* * Output packets in-context until outgoing queue is empty. * Leave responsibility of choosing whether or not to drop the * kernel lock to VIPHYPER_SEND(). */ #define LB_SH 32 static void virtif_start(struct ifnet *ifp) { struct virtif_sc *sc = ifp->if_softc; struct mbuf *m, *m0; struct iovec io[LB_SH]; int i; ifp->if_flags |= IFF_OACTIVE; for (;;) { IF_DEQUEUE(&ifp->if_snd, m0); if (!m0) { break; } m = m0; for (i = 0; i < LB_SH && m; ) { if (m->m_len) { io[i].iov_base = mtod(m, void *); io[i].iov_len = m->m_len; i++; } m = m->m_next; } if (i == LB_SH && m) panic("lazy bum"); bpf_mtap(ifp, m0, BPF_D_OUT); VIFHYPER_SEND(sc->sc_viu, io, i); m_freem(m0); ifp->if_opackets++; } ifp->if_flags &= ~IFF_OACTIVE; } static void virtif_stop(struct ifnet *ifp, int disable) { /* XXX: VIFHYPER_STOP() */ ifp->if_flags &= ~IFF_RUNNING; } void VIF_DELIVERPKT(struct virtif_sc *sc, struct iovec *iov, size_t iovlen) { struct ifnet *ifp = &sc->sc_ec.ec_if; struct ether_header *eth; struct mbuf *m; size_t i; int off, olen; bool passup; const int align = ALIGN(sizeof(struct ether_header)) - sizeof(struct ether_header); if ((ifp->if_flags & IFF_RUNNING) == 0) return; m = m_gethdr(M_NOWAIT, MT_DATA); if (m == NULL) return; /* drop packet */ m->m_len = m->m_pkthdr.len = 0; for (i = 0, off = align; i < iovlen; i++) { olen = m->m_pkthdr.len; m_copyback(m, off, iov[i].iov_len, iov[i].iov_base); off += iov[i].iov_len; if (olen + off != m->m_pkthdr.len) { aprint_verbose_ifnet(ifp, "m_copyback failed\n"); m_freem(m); return; } } m->m_data += align; m->m_pkthdr.len -= align; m->m_len -= align; eth = mtod(m, struct ether_header *); if (memcmp(eth->ether_dhost, CLLADDR(ifp->if_sadl), ETHER_ADDR_LEN) == 0) { passup = true; } else if (ETHER_IS_MULTICAST(eth->ether_dhost)) { passup = true; } else if (ifp->if_flags & IFF_PROMISC) { m->m_flags |= M_PROMISC; passup = true; } else { passup = false; } if (passup) { int bound; m_set_rcvif(m, ifp); KERNEL_LOCK(1, NULL); /* Prevent LWP migrations between CPUs for psref(9) */ bound = curlwp_bind(); if_input(ifp, m); curlwp_bindx(bound); KERNEL_UNLOCK_LAST(NULL); } else { m_freem(m); } m = NULL; } /* * The following ensures that no two modules using if_virt end up with * the same module name. MODULE() and modcmd wrapped in ... bad mojo. */ #define VIF_MOJO(x) MODULE(MODULE_CLASS_DRIVER,x,NULL); #define VIF_MODULE() VIF_MOJO(VIF_BASENAME(if_virt_,VIRTIF_BASE)) #define VIF_MODCMD VIF_BASENAME3(if_virt_,VIRTIF_BASE,_modcmd) VIF_MODULE(); static int VIF_MODCMD(modcmd_t cmd, void *opaque) { int error = 0; switch (cmd) { case MODULE_CMD_INIT: if_clone_attach(&VIF_CLONER); break; case MODULE_CMD_FINI: /* * not sure if interfaces are refcounted * and properly protected */ #if 0 if_clone_detach(&VIF_CLONER); #else error = ENOTTY; #endif break; default: error = ENOTTY; } return error; }