/*
* 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.
*/
/*
* Copyright (c) 2018, 2021 Henning Andersen Matyschok, DARPA/AFRL
*
* 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 ``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 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/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_clone.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/netisr.h>
#include <net/if_types.h>
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if_bridgevar.h>
#include <net/vnet.h>
/*
* Virtual Ethernet interface, ported from OpenBSD. This interface
* operates in conjunction with if_bridge(4).
*/
struct vether_softc {
struct ifmedia sc_ifm; /* fake media information */
struct ifnet *sc_ifp; /* network interface. */
};
#define VETHER_IF_FLAGS (IFF_SIMPLEX|IFF_BROADCAST|IFF_MULTICAST)
#define VETHER_IFCAP_FLAGS (IFCAP_VLAN_MTU|IFCAP_JUMBO_MTU)
#define VETHER_IFM_FLAGS (IFM_ETHER|IFM_AUTO)
/*
* XXX
* The set of protocol-numbers index-set
* against <net/netisr.h> maps-to set
* over (enum), e. g.:
*
* typedef enum netisr_proto {
* netisr_ip = NETISR_IP,
* netisr_igmp = NETISR_IGMP,
* netisr_route = NETISR_ROUTE,
* netisr_arp = NETISR_ARP,
* netisr_ether = NETISR_ETHER,
* netisr_ipv6 = NETISR_IPV6,
* netisr_epair = NETISR_EPAIR,
* netisr_ip_direct = NETISR_IP_DIRECT,
* netisr_ipv6_direct = NETISR_IPV6_DIRECT,
* netisr_vether = NETISR_VETHER_FWD,
* } netisr_proto_t;
*/
#define NETISR_VETHER_FWD 11
typedef enum vether_netisr_component {
vether_netisr_fwd = NETISR_VETHER_FWD,
} vether_netisr_component_t;
static void vether_ifaddr_init(struct ifnet *, struct ether_addr *);
static void vether_nh_fwd_cb(struct mbuf *);
static void vether_texeof(struct ifnet *);
static void vether_init(void *);
static void vether_stop(struct ifnet *);
static void vether_start(struct ifnet *);
static int vether_media_change(struct ifnet *);
static void vether_media_status(struct ifnet *, struct ifmediareq *);
static int vether_ioctl(struct ifnet *, u_long, caddr_t);
static int vether_clone_create(struct if_clone *, int, caddr_t);
static void vether_clone_destroy(struct ifnet *);
static struct netisr_handler vether_nh_fwd = {
.nh_name = "Softintr. for if_vether(4)",
.nh_handler = vether_nh_fwd_cb,
.nh_proto = vether_netisr_fwd,
.nh_policy = NETISR_POLICY_FLOW,
};
static const char vether_name[] = "vether";
VNET_DEFINE(struct if_clone *, vether_cloner);
#define V_vether_cloner VNET(vether_cloner)
static int
vether_clone_create(struct if_clone *ifc, int unit, caddr_t data)
{
struct vether_softc *sc;
struct ifnet *ifp;
struct ether_addr lla;
int error;
/*
* For safety reason, there is a condition test applied -
* independently, if M_WAITOK was enabled or not.
*/
if ((sc = malloc(sizeof(struct vether_softc),
M_DEVBUF, M_WAITOK|M_ZERO)) != NULL) {
if ((ifp = if_alloc(IFT_ETHER)) != NULL) {
sc->sc_ifp = ifp;
ifp->if_softc = sc;
if_initname(ifp, vether_name, unit);
ifp->if_init = vether_init;
ifp->if_ioctl = vether_ioctl;
ifp->if_start = vether_start;
ifp->if_flags = VETHER_IF_FLAGS;
ifp->if_capabilities = VETHER_IFCAP_FLAGS;
ifp->if_capenable = VETHER_IFCAP_FLAGS;
ifp->if_baudrate = 0;
ifmedia_init(&sc->sc_ifm, 0, vether_media_change,
vether_media_status);
ifmedia_add(&sc->sc_ifm, VETHER_IFM_FLAGS, 0, NULL);
ifmedia_set(&sc->sc_ifm, VETHER_IFM_FLAGS);
vether_ifaddr_init(ifp, &lla);
ether_ifattach(ifp, lla.octet);
error = 0;
} else {
error = ENOSPC;
free(sc, M_DEVBUF);
}
} else
error = ENOBUFS;
return (error);
}
static void
vether_clone_destroy(struct ifnet *ifp)
{
struct vether_softc *sc;
if (ifp != NULL) {
ifp->if_flags &= ~IFF_UP;
vether_stop(ifp);
if ((sc = ifp->if_softc) != NULL) {
free(sc, M_DEVBUF);
ifp->if_softc = NULL;
}
ether_ifdetach(ifp);
if_free(ifp);
}
}
static void
vnet_vether_init(const void *unused __unused)
{
V_vether_cloner = if_clone_simple(vether_name,
vether_clone_create, vether_clone_destroy, 0);
}
VNET_SYSINIT(vnet_vether_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
vnet_vether_init, NULL);
static void
vnet_vether_uninit(const void *unused __unused)
{
if_clone_detach(V_vether_cloner);
}
VNET_SYSUNINIT(vnet_vether_uninit, SI_SUB_PSEUDO, SI_ORDER_ANY,
vnet_vether_uninit, NULL);
static int
vether_mod_event(module_t mod, int event, void *data)
{
switch (event) {
case MOD_LOAD:
netisr_register(&vether_nh_fwd);
return (0);
case MOD_UNLOAD:
netisr_unregister(&vether_nh_fwd);
return (0);
default:
break;
}
return (EOPNOTSUPP);
}
static moduledata_t vether_mod = {
"if_vether",
vether_mod_event,
0
};
DECLARE_MODULE(if_vether, vether_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
/*
* I/O.
*/
static void
vether_txeof(struct ifnet *ifp)
{
struct mbuf *m;
if (ifp != NULL) {
IFQ_DEQUEUE(&ifp->if_snd, m);
if (m != NULL) {
BPF_MTAP(ifp, m);
if ((m->m_pkthdr & M_PKTHDR) != 0) {
/* do some statistics */
if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
/* discard, if not member of if_bridge(4) */
if (ifp->if_bridge == NULL)
m->m_pkthdr.rcvif = ifp;
/*
* Three cases are considered here:
*
* (a) Frame was tx'd by layer above.
*
* (b) Frame was rx'd by link-layer.
*
* (c) Data sink.
*/
if (m->m_pkthdr.rcvif == NULL) {
m->m_pkthdr.rcvif = ifp;
netisr_dispatch(vether_netisr_fwd, m);
} else if (m->m_pkthdr.rcvif != ifp) {
m->m_pkthdr.rcvif = ifp;
/* demultiplex any other frame */
(*ifp->if_input)(ifp, m);
} else {
m_freem(m);
m = NULL;
}
vether_txeof(ifp);
} else {
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
m_freem(m);
m = NULL;
}
}
}
}
static void
vether_start(struct ifnet *ifp)
{
if ((ifp->if_flags & IFF_UP) != 0) {
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
vether_txeof(ifp);
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
}
}
/*
* Broadcast frame by if_bridge(4).
*/
static void
vether_nh_fwd_cb(struct mbuf *m)
{
struct ifnet *ifp;
int error;
if (m != NULL) {
if ((m->m_flags & M_PKTHDR) != 0) {
if ((ifp = m->m_pkthdr.rcvif) != NULL)
BRIDGE_OUTPUT(ifp, m, error);
else
error = EADDRNOTAVAIL;
} else
error = ECONNABORTED;
} else
error = ENOBUFS;
if (error != 0) {
if (m != NULL) {
m_freem(m);
m = NULL;
}
}
}
/*
* Initialize lla.
*/
static void
vether_ifaddr_init(struct ifnet *ifp, struct ether_addr *lla)
{
caddr_t pfx, sfx;
if (ifp != NULL) {
if (lla != NULL) {
pfx = lla->octet;
pfx[0] = 0x02;
sfx = (pfx + 1);
arc4rand(sfx, 5, 0);
}
}
}
static int
vether_media_change(struct ifnet *ifp)
{
int error;
if (ifp != NULL)
error = 0;
else
error = EADDRNOTAVAIL;
return (error);
}
static void
vether_media_status(struct ifnet *ifp, struct ifmediareq *ifm)
{
if (ifp != NULL) {
if (ifm != NULL) {
ifm->ifm_active = (IFM_ETHER|IFM_AUTO);
ifm->ifm_status = (IFM_AVALID|IFM_ACTIVE);
}
}
}
static void
vether_init(void *xsc)
{
struct vether_softc *sc;
struct ifnet *ifp;
if ((sc = (struct vether_softc *)xsc) != NULL) {
if ((ifp = sc->sc_ifp) != NULL) {
ifp->if_drv_flags |= IFF_DRV_RUNNING;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
}
}
}
static void
vether_stop(struct ifnet *ifp)
{
if (ifp != NULL)
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING|IFF_DRV_OACTIVE);
}
static int
vether_ioctl(struct ifnet *ifp, u_long req, caddr_t argp)
{
struct vether_softc *sc;
struct ifreq *ifr;
int error;
if (ifp != NULL) {
if ((sc = ifp->if_softc) != NULL) {
ifr = (struct ifreq *)argp;
switch (req) {
case SIOCSIFMTU:
if (ifr->ifr_mtu < ETHER_MAX_LEN_JUMBO) {
ifp->if_mtu = ifr->ifr_mtu;
error = 0;
} else
error = EINVAL;
break;
case SIOCSIFMEDIA: /* Media types can't be changed. */
case SIOCGIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifm, req);
break;
case SIOCSIFFLAGS:
case SIOCADDMULTI:
case SIOCDELMULTI:
error = 0;
break;
case SIOCSIFPHYS:
error = EOPNOTSUPP;
break;
default:
error = ether_ioctl(ifp, req, argp);
break;
}
} else
error = ENXIO;
} else
error = ENOTTY;
return (error);
}