kern/179473: Source code contribution of implementation about virtual ethernet interface
Henning Matyschok
henning.matyschok at stud.fh-flensburg.de
Mon Jun 10 23:20:00 UTC 2013
>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 at 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:
More information about the freebsd-bugs
mailing list