kern/179473: Source code contribution of implementation about virtual ethernet interface
Henning Matyschok
henning.matyschok at stud.fh-flensburg.de
Thu Jun 20 00:40:02 UTC 2013
The following reply was made to PR kern/179473; it has been noted by GNATS.
From: "Henning Matyschok" <henning.matyschok at stud.fh-flensburg.de>
To: bug-followup at freebsd.org, henning.matyschok at stud.fh-flensburg.de
Cc:
Subject: Re: kern/179473: Source code contribution of implementation about
virtual ethernet interface
Date: Thu, 20 Jun 2013 00:34:17 -0000
------------yPGQsOt2uvF1CxHxvIlgW8
Content-Type: text/plain; charset=us-ascii; format=flowed; delsp=yes
Content-Transfer-Encoding: 7bit
I'm excusing for my bad english.
------------yPGQsOt2uvF1CxHxvIlgW8
Content-Disposition: attachment; filename=if_veth.c.txt
Content-Type: text/plain; name=if_veth.c.txt
Content-Transfer-Encoding: 7bit
/*-
* 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/netisr.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 *, struct mbuf **);
/*
* 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;
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_internal(struct ifnet *, struct mbuf *);
static void veth_input(struct ifnet *, struct mbuf *);
static int veth_media_change(struct ifnet *);
static void veth_media_status(struct ifnet *ifp, struct ifmediareq *);
static int veth_ioctl(struct ifnet *, u_long, caddr_t);
static void veth_start(struct ifnet *);
static void veth_stop(struct ifnet *, int);
/* See net/netisr.h for further deteils. */
#define NETISR_VETH 13
static void veth_nh_input(struct mbuf *);
/* Netisr handler definition. */
static struct netisr_handler veth_nh = {
.nh_name = VETHNAME,
.nh_handler = veth_nh_input,
.nh_proto = NETISR_VETH,
.nh_policy = NETISR_POLICY_SOURCE,
.nh_dispatch = NETISR_DISPATCH_DIRECT,
};
/*
* 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);
netisr_register(&veth_nh);
if_clone_attach(&veth_cloner);
break;
case MOD_UNLOAD:
if_clone_detach(&veth_cloner);
netisr_unregister(&veth_nh);
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);
/*
* 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;
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);
}
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;
}
/*
* 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);
}
/*
* 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;
}
/*
* Dispatches transmissions of queued frames to bridge(4) or to
* itself when bridge(4) forwarded frames to this interface.
*/
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.
*/
static void
veth_input_internal(struct ifnet *ifp, struct mbuf *m)
{
struct ether_header *eh;
uint16_t type;
if ((ifp->if_flags & IFF_UP) == 0) {
goto bad;
}
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_ifnet_create_mbuf(ifp, m);
#endif /* MAC */
ETHER_BPF_MTAP(ifp, m);
ifp->if_ibytes += m->m_pkthdr.len;
ifp->if_ipackets++;
if (ifp->if_flags & IFF_MONITOR) {
goto drop;
}
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;
}
}
}
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_input_p) {
if (IFP2AC(ifp)->ac_netgraph) {
(*ng_ether_input_p)(ifp, &m);
if (m == NULL) {
return;
}
}
}
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;
}
}
ether_demux(ifp, m);
return;
drop:
ifp->if_ierrors++;
bad:
m_freem(m);
}
static void
veth_nh_input(struct mbuf *m)
{
veth_input_internal(m->m_pkthdr.rcvif, m);
}
static void
veth_input(struct ifnet *ifp, struct mbuf *m)
{
netisr_dispatch(NETISR_VETH, m);
}
------------yPGQsOt2uvF1CxHxvIlgW8--
More information about the freebsd-bugs
mailing list