/* $OpenBSD: if_vether.c,v 1.15 2010/04/02 21:45:49 deraadt Exp $ */ /* * Copyright (c) 2009 Theo de Raadt * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif void vetherattach(int); int vetherioctl(struct ifnet *, u_long, caddr_t); void vetherstart(struct ifnet *); int vether_clone_create(struct if_clone *, int); int vether_clone_destroy(struct ifnet *); int vether_media_change(struct ifnet *); void vether_media_status(struct ifnet *, struct ifmediareq *); struct vether_softc { struct arpcom sc_ac; struct ifmedia sc_media; }; struct if_clone vether_cloner = IF_CLONE_INITIALIZER("vether", vether_clone_create, vether_clone_destroy); int vether_media_change(struct ifnet *ifp) { return (0); } void vether_media_status(struct ifnet *ifp, struct ifmediareq *imr) { imr->ifm_active = IFM_ETHER | IFM_AUTO; imr->ifm_status = IFM_AVALID | IFM_ACTIVE; } void vetherattach(int nvether) { if_clone_attach(&vether_cloner); } int vether_clone_create(struct if_clone *ifc, int unit) { struct ifnet *ifp; struct vether_softc *sc; if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) return (ENOMEM); ifp = &sc->sc_ac.ac_if; snprintf(ifp->if_xname, sizeof ifp->if_xname, "vether%d", unit); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ether_fakeaddr(ifp); ifp->if_softc = sc; ifp->if_ioctl = vetherioctl; ifp->if_start = vetherstart; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); IFQ_SET_READY(&ifp->if_snd); ifp->if_capabilities = IFCAP_VLAN_MTU; ifmedia_init(&sc->sc_media, 0, vether_media_change, vether_media_status); ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL); ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO); if_attach(ifp); ether_ifattach(ifp); return (0); } int vether_clone_destroy(struct ifnet *ifp) { struct vether_softc *sc = ifp->if_softc; ether_ifdetach(ifp); if_detach(ifp); free(sc, M_DEVBUF); return (0); } /* * The bridge has magically already done all the work for us, * and we only need to discard the packets. */ void vetherstart(struct ifnet *ifp) { struct mbuf *m; int s; for (;;) { s = splnet(); IFQ_DEQUEUE(&ifp->if_snd, m); splx(s); if (m == NULL) return; ifp->if_opackets++; m_freem(m); } } /* ARGSUSED */ int vetherioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct vether_softc *sc = (struct vether_softc *)ifp->if_softc; #ifdef INET struct ifaddr *ifa = (struct ifaddr *)data; #endif struct ifreq *ifr = (struct ifreq *)data; int error = 0, link_state; switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; #ifdef INET if (ifa->ifa_addr->sa_family == AF_INET) arp_ifinit(&sc->sc_ac, ifa); #endif /* FALLTHROUGH */ case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { ifp->if_flags |= IFF_RUNNING; link_state = LINK_STATE_UP; } else { ifp->if_flags &= ~IFF_RUNNING; link_state = LINK_STATE_DOWN; } if (ifp->if_link_state != link_state) { ifp->if_link_state = link_state; if_link_state_change(ifp); } break; case SIOCADDMULTI: case SIOCDELMULTI: { if (ifr == 0) { error = EAFNOSUPPORT; /* XXX */ break; } error = (cmd == SIOCADDMULTI) ? ether_addmulti(ifr, &sc->sc_ac) : ether_delmulti(ifr, &sc->sc_ac); if (error == ENETRESET) { /* * Multicast list has changed; set the hardware * filter accordingly. The good thing is we do * not have a hardware filter (: */ error = 0; } break; } case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); break; default: error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); } return (error); }