Date: Mon, 10 Jun 2013 23:19:06 GMT From: Henning Matyschok <henning.matyschok@stud.fh-flensburg.de> To: freebsd-gnats-submit@FreeBSD.org Subject: kern/179473: Source code contribution of implementation about virtual ethernet interface Message-ID: <201306102319.r5ANJ6sm095594@oldred.freebsd.org> Resent-Message-ID: <201306102320.r5ANK0KM043998@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 179473 >Category: kern >Synopsis: Source code contribution of implementation about virtual ethernet interface >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: update >Submitter-Id: current-users >Arrival-Date: Mon Jun 10 23:20:00 UTC 2013 >Closed-Date: >Last-Modified: >Originator: Henning Matyschok >Release: RELENG_9_1 >Organization: >Environment: FreeBSD marduk.testenv.local 9.0-RELEASE-p4 FreeBSD 9.0-RELEASE-p4 #0: Sun Mar 3 19:11:46 CET 2013 supervisor@marduk.testenv.local:/usr/obj/usr/src/sys/MARDUK i386 >Description: ng_eiface(4) is not capable to interact with ng_pppoe(4) throught ng_ether(4). >How-To-Repeat: Try to use an instance of ng_eiface(4) as link layer interface for userland ppp. >Fix: I've written a virtual Ethernet interface (if_veth.c) wich uses bridge(4) to access link layer. This is capable to interact with ng_pppoe(4) by ng_ether(4). Now it is possible to use virtual Ethernet interfaces as link layer interface for userland ppp. Either, see # # http://wiki.bsdforen.de/wiki:user:marduk#if_vethc # or see attachment. Patch attached with submission follows: /*- * Copyright (c) 2013 Henning Matyschok * 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 AND CONTRIBUTORS ``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. * * Copyright (c) 1982, 1989, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS 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 <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/libkern.h> #include <sys/socket.h> #include <sys/sockio.h> #include <net/if.h> #include <net/if_clone.h> #include <net/if_media.h> #include <net/if_types.h> #include <net/if_var.h> #include <net/ethernet.h> #include <net/if_bridgevar.h> #include <net/if_vlan_var.h> #include <net/if_dl.h> #include <net/if_arp.h> #include <net/bpf.h> #include <netinet/in.h> #include <netinet/in_var.h> #include <netinet/ip_carp.h> #ifdef MAC #include <security/mac/mac_framework.h> #endif /* MAC */ static const uint8_t ether_bcast_lla[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /* if_lagg(4) entry point. */ extern struct mbuf *(*lagg_input_p)(struct ifnet *, struct mbuf *); /* ng_ether(4) entry point. */ extern void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); /* * Declaration of reduced struct bridge_softc is needed to identify * bridge(4) describing ifnet structure on the fly, when veth_start * may called. See net/if_bridge.c and net/if_var.h for further * details. */ struct bridge_softc { struct ifnet *sc_ifp; }; #define VETHNAME "veth" struct veth_softc { struct ifnet *sc_ifp; /* It will be a network interface. */ LIST_ENTRY(veth_softc) veth_list; struct mtx sc_mtx; #define VETH_LOCK_INIT(sc) mtx_init(&(sc)->sc_mtx, "veth softc", \ NULL, MTX_DEF) #define VETH_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) #define VETH_LOCK(sc) mtx_lock(&(sc)->sc_mtx) #define VETH_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) #define VETH_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) /* Fake information about used transmission media. */ struct ifmedia sc_ifm; int sc_status; }; static LIST_HEAD(, veth_softc) veth_list; static struct mtx veth_mtx; static MALLOC_DEFINE(M_VETH, VETHNAME, "Virtual ethernet interface"); /* Ifc cloner specific subr. */ static int veth_clone_create(struct if_clone *, int, caddr_t); static void veth_clone_destroy(struct ifnet *); IFC_SIMPLE_DECLARE(veth, 0); /* Interface specific methods. */ static void veth_init(void *); static void veth_input(struct ifnet *ifp, struct mbuf *m); static int veth_ioctl(struct ifnet *, u_long, caddr_t); static void veth_start(struct ifnet *); static void veth_stop(struct ifnet *, int); /* * Module event handler. */ static int veth_mod_event(module_t mod, int event, void *data) { int error = 0; switch (event) { case MOD_LOAD: mtx_init(&veth_mtx, "if_veth", NULL, MTX_DEF); if_clone_attach(&veth_cloner); break; case MOD_UNLOAD: if_clone_detach(&veth_cloner); mtx_destroy(&veth_mtx); break; default: error = EOPNOTSUPP; } return(error); } static moduledata_t veth_mod = { "if_veth", veth_mod_event, 0 }; DECLARE_MODULE(if_veth, veth_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); /* * By SIOCSIFMEDIA and SIOCGIFMEDIA ioctl requests invoked callbacks. */ static int veth_media_change(struct ifnet *ifp) { return(0); } static void veth_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) { ifmr->ifm_status = IFM_AVALID|IFM_ACTIVE; ifmr->ifm_active = IFM_ETHER|IFM_1000_T|IFM_FDX; } /* * Instantiates veth interface. */ static int veth_clone_create(struct if_clone *ifc, int unit, caddr_t data) { struct veth_softc *sc; struct ifnet *ifp; uint32_t randval; uint8_t lla[ETHER_ADDR_LEN]; sc = malloc(sizeof(struct veth_softc), M_VETH, M_WAITOK|M_ZERO); ifp = sc->sc_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { free(sc, M_VETH); return(ENOSPC); } VETH_LOCK_INIT(sc); ifp->if_softc = sc; if_initname(ifp, ifc->ifc_name, unit); /* Generates lla with randomized value. */ lla[0] = 0x0; randval = arc4random(); memcpy(&lla[1], &randval, sizeof(uint32_t)); lla[5] = (uint8_t)unit; /* Interface major number */ ether_ifattach(ifp, lla); ifp->if_init = veth_init; ifp->if_input = veth_input; ifp->if_ioctl = veth_ioctl; ifp->if_start = veth_start; ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_JUMBO_MTU; ifp->if_capenable = IFCAP_VLAN_MTU | IFCAP_JUMBO_MTU; ifp->if_flags = (IFF_SIMPLEX|IFF_BROADCAST|IFF_MULTICAST); ifp->if_snd.ifq_maxlen = ifqmaxlen; /* * Initializes by in context of SIOCSIFMEDIA and SIOCGIFMEDIA * ioctl requests used callbacks and adds faked media information * about non-existing physical transmission media of non-existing * hardware. */ ifmedia_init(&sc->sc_ifm, 0, veth_media_change, veth_media_status); ifmedia_add(&sc->sc_ifm, IFM_ETHER|IFM_1000_T|IFM_FDX, 0, NULL); ifmedia_set(&sc->sc_ifm, IFM_ETHER|IFM_1000_T|IFM_FDX); sc->sc_status = IFM_AVALID; ifp->if_baudrate = 0; mtx_lock(&veth_mtx); LIST_INSERT_HEAD(&veth_list, sc, veth_list); mtx_unlock(&veth_mtx); ifp->if_drv_flags |= IFF_DRV_RUNNING; return(0); } /* * Destroys veth interface. */ static void veth_clone_destroy(struct ifnet *ifp) { struct veth_softc *sc; sc = ifp->if_softc; mtx_lock(&veth_mtx); veth_stop(ifp, 1); ifp->if_flags &= ~IFF_UP; LIST_REMOVE(sc, veth_list); ether_ifdetach(ifp); VETH_LOCK_DESTROY(sc); mtx_unlock(&veth_mtx); free(sc, M_VETH); } /* * Initializes veth interface. */ static void veth_init(void *xsc) { struct veth_softc *sc; struct ifnet *ifp; sc = (struct veth_softc *)xsc; ifp = sc->sc_ifp; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { return; } VETH_LOCK(sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; VETH_UNLOCK(sc); } /* * Stops veth interface. */ static void veth_stop(struct ifnet *ifp, int disable) { struct veth_softc *sc; sc = ifp->if_softc; VETH_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { return; } ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } /* * Handles ioctl requests. Media types can't changed, * this is a virtual ethernet interface. */ static int veth_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct veth_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int error = 0; VETH_LOCK(sc); switch (cmd) { case SIOCSIFMTU: if (ifr->ifr_mtu > ETHER_MAX_LEN_JUMBO) { error = EINVAL; } else { ifp->if_mtu = ifr->ifr_mtu; } break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifm, cmd); break; case SIOCSIFFLAGS: case SIOCADDMULTI: case SIOCDELMULTI: break; case SIOCSIFPHYS: error = EOPNOTSUPP; break; default: error = ether_ioctl(ifp, cmd, data); break; } VETH_UNLOCK(sc); return(error); } /* * Dispatches transmissions of queued frames to bridge(4) by * forwarding them throught veth_start() when frame transmission * starts by ether_frame_output(). * * Normally, an ethernet frame may dequeued and processed by * network driver specific xxx_output() method (i. e. rl_start(), * defined in pci/if_rl.c) via if_transmit() method, wich * initializes enqueueing of frames temporarly. * * But in this case, there exists no underlying hardware, this * implies that if_transmit() of bridge(4) invocates bridge_start * to forward by this network interface transmitted frames to * real networking devices or other vitual networking devices * wich are capable to be a member (i. e. gif(4)) of an instance * of bridge(4). * * Finally, frames may dispatched to itself through call of * veth_input() when frame comes from bridge(4). Otherwise, * frames may discarded silently, when this interface is not * member of bridge(4) or when frames are reinjected by bridge(4) * throught bridge_start(). */ static void veth_start(struct ifnet *ifp) { struct mbuf *m; struct bridge_softc *sc; struct ifnet *bifp; ifp->if_drv_flags |= IFF_DRV_OACTIVE; for (;;) { IFQ_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { break; } if (ifp->if_bridge) { sc = ifp->if_bridge; bifp = sc->sc_ifp; if (m->m_pkthdr.rcvif == NULL) { m->m_pkthdr.rcvif = ifp; ETHER_BPF_MTAP(ifp, m); ifp->if_obytes += m->m_pkthdr.len; ifp->if_opackets++; (bifp->if_transmit)(bifp, m); } else if (m->m_pkthdr.rcvif == ifp) { m_freem(m); } else { (ifp->if_input)(ifp, m); } } else { m_freem(m); } } ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } /* * Handles input of frames. This method is derived * from ether_input_internal where its implementation * remains in net/if_ethersubr.c. Possibly, the * Regents of University of California owns the * copyright about this function, because I'm adopted * most of all code from it. */ static void veth_input(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; uint16_t type; if ((ifp->if_flags & IFF_UP) == 0) { goto bad; } /* * Do some consistency checks about frame header * related stuff (If exists, size, offset, etc.). */ if ((m->m_flags & M_PKTHDR) == 0) { goto drop; } if (m->m_len < ETHER_HDR_LEN) { m = m_pullup(m, ETHER_HDR_LEN); if (m == NULL) { goto drop; } } eh = mtod(m, struct ether_header *); type = ntohs(eh->ether_type); if (m->m_pkthdr.rcvif == NULL) { goto drop; } if (ETHER_IS_MULTICAST(eh->ether_dhost)) { if (bcmp(ether_bcast_lla, eh->ether_dhost, ETHER_ADDR_LEN) == 0) { m->m_flags |= M_BCAST; } else { m->m_flags |= M_MCAST; } ifp->if_imcasts++; } #ifdef MAC /* * MAC policy driven mbuf tagging. */ mac_ifnet_create_mbuf(ifp, m); #endif /* MAC */ /* * Processing by bpf(4) is possible and do some statistics. */ ETHER_BPF_MTAP(ifp, m); ifp->if_ibytes += m->m_pkthdr.len; ifp->if_ipackets++; if (ifp->if_flags & IFF_MONITOR) { goto drop; } /* * lagg(4) handling. */ if (lagg_input_p) { if (ifp->if_type == IFT_IEEE8023ADLAG) { m = (*lagg_input_p)(ifp, m); if (m != NULL) { ifp = m->m_pkthdr.rcvif; } else { return; } } } /* * Processing of 802.1Q tag, if vlan frames may occour. \ */ if ((m->m_flags & M_VLANTAG) == 0 && type == ETHERTYPE_VLAN) { struct ether_vlan_header *evl; if (m->m_len < sizeof(*evl) && (m = m_pullup(m, sizeof(*evl))) == NULL) { goto drop; } evl = mtod(m, struct ether_vlan_header *); m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag); m->m_flags |= M_VLANTAG; bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN, ETHER_HDR_LEN - ETHER_TYPE_LEN); m_adj(m, ETHER_VLAN_ENCAP_LEN); } M_SETFIB(m, ifp->if_fib); /* * If ng_ether(4) node exist, then lets netgraph(4) * doing frame processing. */ if (ng_ether_input_p) { if (IFP2AC(ifp)->ac_netgraph) { (*ng_ether_input_p)(ifp, &m); if (m == NULL) { return; } } } /* * Finishes off M_PROMISC flag for processing * SDU through carp(4). */ if (ifp->if_carp && (*carp_forus_p)(ifp, eh->ether_dhost)) { m->m_flags &= ~M_PROMISC; } else { if (!ETHER_IS_MULTICAST(eh->ether_dhost) && bcmp(IF_LLADDR(ifp), eh->ether_dhost, ETHER_ADDR_LEN) != 0) { m->m_flags |= M_PROMISC; } } /* * Upper lzyer processing of received frame starts here. */ ether_demux(ifp, m); return; drop: ifp->if_ierrors++; bad: m_freem(m); } >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201306102319.r5ANJ6sm095594>