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