This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
zhangyang-zerotierone/ext/tap-mac/tuntap/src/tuntap.cc

967 lines
21 KiB
C++

/*
* ip tunnel/ethertap device for MacOSX. Common functionality of tap_interface and tun_interface.
*
* tuntap_interface class definition
*/
/*
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
*
* 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.
* 3. The name of the author may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 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 "tuntap.h"
#if 0
#define dprintf(...) log(LOG_INFO, __VA_ARGS__)
#else
#define dprintf(...)
#endif
extern "C" {
#include <sys/conf.h>
#include <sys/syslog.h>
#include <sys/param.h>
#include <sys/filio.h>
#include <sys/sockio.h>
#include <sys/fcntl.h>
#include <sys/kpi_socket.h>
#include <vm/vm_kern.h>
#include <net/if_types.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_arp.h>
#include <miscfs/devfs/devfs.h>
}
extern "C" {
/* interface service functions that delegate to the appropriate tuntap_interface instance */
errno_t
tuntap_if_output(ifnet_t ifp, mbuf_t m)
{
if (ifp != NULL) {
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
if (ttif != NULL)
return ttif->if_output(m);
}
if (m != NULL)
mbuf_freem_list(m);
return ENODEV;
}
errno_t
tuntap_if_ioctl(ifnet_t ifp, long unsigned int cmd, void *arg)
{
if (ifp != NULL) {
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
if (ttif != NULL)
return ttif->if_ioctl(cmd, arg);
}
return ENODEV;
}
errno_t
tuntap_if_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t))
{
if (ifp != NULL) {
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
if (ttif != NULL)
return ttif->if_set_bpf_tap(mode, cb);
}
return ENODEV;
}
errno_t
tuntap_if_demux(ifnet_t ifp, mbuf_t m, char *header, protocol_family_t *proto)
{
if (ifp != NULL) {
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
if (ttif != NULL)
return ttif->if_demux(m, header, proto);
}
return ENODEV;
}
errno_t
tuntap_if_framer(ifnet_t ifp, mbuf_t *m, const struct sockaddr *dest, const char *dest_linkaddr,
const char *frame_type)
{
if (ifp != NULL) {
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
if (ttif != NULL)
return ttif->if_framer(m, dest, dest_linkaddr, frame_type);
}
return ENODEV;
}
errno_t
tuntap_if_add_proto(ifnet_t ifp, protocol_family_t proto, const struct ifnet_demux_desc *ddesc,
u_int32_t ndesc)
{
if (ifp != NULL) {
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
if (ttif != NULL)
return ttif->if_add_proto(proto, ddesc, ndesc);
}
return ENODEV;
}
errno_t
tuntap_if_del_proto(ifnet_t ifp, protocol_family_t proto)
{
if (ifp != NULL) {
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
if (ttif != NULL)
return ttif->if_del_proto(proto);
}
return ENODEV;
}
errno_t
tuntap_if_check_multi(ifnet_t ifp, const struct sockaddr* maddr)
{
if (ifp != NULL)
{
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
if (ttif != NULL)
return ttif->if_check_multi(maddr);
}
return ENODEV;
}
void
tuntap_if_detached(ifnet_t ifp)
{
if (ifp != NULL) {
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
if (ttif != NULL)
ttif->if_detached();
}
}
errno_t
tuntap_if_noop_output(ifnet_t, mbuf_t)
{
return ENODEV;
}
errno_t
tuntap_if_noop_demux(ifnet_t, mbuf_t, char*, protocol_family_t*)
{
return ENODEV;
}
errno_t
tuntap_if_noop_add_proto(ifnet_t, protocol_family_t, const struct ifnet_demux_desc*, u_int32_t)
{
return ENODEV;
}
errno_t
tuntap_if_noop_del_proto(ifnet_t, protocol_family_t)
{
return ENODEV;
}
} /* extern "C" */
/* tuntap_mbuf_queue */
tuntap_mbuf_queue::tuntap_mbuf_queue()
{
head = tail = NULL;
size = 0;
}
tuntap_mbuf_queue::~tuntap_mbuf_queue()
{
clear();
}
bool
tuntap_mbuf_queue::enqueue(mbuf_t mb)
{
if (size == QUEUE_SIZE)
return false;
mbuf_setnextpkt(mb, NULL);
if (head == NULL)
head = tail = mb;
else {
mbuf_setnextpkt(tail, mb);
tail = mb;
}
size++;
return true;
}
mbuf_t
tuntap_mbuf_queue::dequeue()
{
mbuf_t ret;
/* check wether there is a packet in the queue */
if (head == NULL)
return NULL;
/* fetch it */
ret = head;
head = mbuf_nextpkt(head);
mbuf_setnextpkt(ret, NULL);
size--;
return ret;
}
void
tuntap_mbuf_queue::clear()
{
/* free mbufs that are in the queue */
if (head != NULL)
mbuf_freem_list(head);
head = NULL;
tail = NULL;
size = 0;
}
/* tuntap_interface members */
tuntap_interface::tuntap_interface()
{
/* initialize the members */
ifp = NULL;
open = false;
block_io = true;
dev_handle = NULL;
pid = 0;
selthreadclear(&rsel);
bpf_mode = BPF_MODE_DISABLED;
bpf_callback = NULL;
bzero(unique_id, UIDLEN);
in_ioctl = false;
}
tuntap_interface::~tuntap_interface()
{
}
bool
tuntap_interface::register_chardev(unsigned short major)
{
/* register character device */
dev_handle = devfs_make_node(makedev(major, unit), DEVFS_CHAR, 0, 0, 0660, "%s%d",
family_name, (int) unit);
if (dev_handle == NULL) {
log(LOG_ERR, "tuntap: could not make /dev/%s%d\n", family_name, (int) unit);
return false;
}
return true;
}
void
tuntap_interface::unregister_chardev()
{
dprintf("unregistering character device\n");
/* unregister character device */
if (dev_handle != NULL)
devfs_remove(dev_handle);
dev_handle = NULL;
}
bool
tuntap_interface::register_interface(const struct sockaddr_dl* lladdr, void *bcaddr,
u_int32_t bcaddrlen)
{
struct ifnet_init_params ip;
errno_t err;
dprintf("register_interface\n");
/* initialize an initialization info struct */
ip.uniqueid_len = UIDLEN;
ip.uniqueid = unique_id;
ip.name = family_name;
ip.unit = unit;
ip.family = family;
ip.type = type;
ip.output = tuntap_if_output;
ip.demux = tuntap_if_demux;
ip.add_proto = tuntap_if_add_proto;
ip.del_proto = tuntap_if_del_proto;
ip.check_multi = tuntap_if_check_multi;
ip.framer = tuntap_if_framer;
ip.softc = this;
ip.ioctl = tuntap_if_ioctl;
ip.set_bpf_tap = tuntap_if_set_bpf_tap;
ip.detach = tuntap_if_detached;
ip.event = NULL;
ip.broadcast_addr = bcaddr;
ip.broadcast_len = bcaddrlen;
dprintf("tuntap: tuntap_if_check_multi is at 0x%08x\n", (void*) tuntap_if_check_multi);
/* allocate the interface */
err = ifnet_allocate(&ip, &ifp);
if (err) {
log(LOG_ERR, "tuntap: could not allocate interface for %s%d: %d\n", family_name,
(int) unit, err);
ifp = NULL;
return false;
}
/* activate the interface */
err = ifnet_attach(ifp, lladdr);
if (err) {
log(LOG_ERR, "tuntap: could not attach interface %s%d: %d\n", family_name,
(int) unit, err);
ifnet_release(ifp);
ifp = NULL;
return false;
}
dprintf("setting interface flags\n");
/* set interface flags */
ifnet_set_flags(ifp, IFF_RUNNING | IFF_MULTICAST | IFF_SIMPLEX, (u_int16_t) ~0UL);
dprintf("flags: %x\n", ifnet_flags(ifp));
return true;
}
void
tuntap_interface::unregister_interface()
{
errno_t err;
dprintf("unregistering network interface\n");
if (ifp != NULL) {
interface_detached = false;
/* detach interface */
err = ifnet_detach(ifp);
if (err)
log(LOG_ERR, "tuntap: error detaching interface %s%d: %d\n",
family_name, unit, err);
dprintf("interface detaching\n");
/* Wait until the interface has completely been detached. */
detach_lock.lock();
while (!interface_detached)
detach_lock.sleep(&interface_detached);
detach_lock.unlock();
dprintf("interface detached\n");
/* release the interface */
ifnet_release(ifp);
ifp = NULL;
}
dprintf("network interface unregistered\n");
}
void
tuntap_interface::cleanup_interface()
{
errno_t err;
ifaddr_t *addrs;
ifaddr_t *a;
struct ifreq ifr;
/* mark the interface down */
ifnet_set_flags(ifp, 0, IFF_UP | IFF_RUNNING);
/* Unregister all interface addresses. This works around a deficiency in the Darwin kernel.
* If we don't remove all IP addresses that are attached to the interface it can happen that
* the IP code fails to clean them up itself. When the interface is recycled, the IP code
* might then think some addresses are still attached to the interface...
*/
err = ifnet_get_address_list(ifp, &addrs);
if (!err) {
/* Execute a SIOCDIFADDR ioctl for each address. For technical reasons, we can only
* do that with a socket of the appropriate family. So try to create a dummy socket.
* I know this is a little expensive, but better than crashing...
*
* This really sucks.
*/
for (a = addrs; *a != NULL; a++) {
/* initialize the request parameters */
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d",
ifnet_name(ifp), ifnet_unit(ifp));
ifaddr_address(*a, &(ifr.ifr_addr), sizeof(ifr.ifr_addr));
if (ifr.ifr_addr.sa_family != AF_INET)
continue;
dprintf("trying to delete address of family %d\n", ifr.ifr_addr.sa_family);
do_sock_ioctl(ifr.ifr_addr.sa_family, SIOCDIFADDR, &ifr);
}
/* release the address list */
ifnet_free_address_list(addrs);
}
}
bool
tuntap_interface::idle()
{
return !(open);
}
void
tuntap_interface::notify_bpf(mbuf_t mb, bool out)
{
auto_lock l(&bpf_lock);
if ((out && bpf_mode == BPF_MODE_OUTPUT)
|| (!out && bpf_mode == BPF_MODE_INPUT)
|| (bpf_mode == BPF_MODE_INPUT_OUTPUT))
(*bpf_callback)(ifp, mb);
}
void
tuntap_interface::do_sock_ioctl(sa_family_t af, unsigned long cmd, void* arg) {
if (in_ioctl) {
log(LOG_ERR, "tuntap: ioctl recursion detected, aborting.\n");
return;
}
socket_t sock;
errno_t err = sock_socket(af, SOCK_RAW, 0, NULL, NULL, &sock);
if (err) {
log(LOG_ERR, "tuntap: failed to create socket: %d\n", err);
return;
}
in_ioctl = true;
/* issue the ioctl */
err = sock_ioctl(sock, cmd, arg);
if (err)
log(LOG_ERR, "tuntap: socket ioctl %d failed: %d\n", cmd, err);
in_ioctl = false;
/* get rid of the socket */
sock_close(sock);
}
/* character device service methods */
int
tuntap_interface::cdev_open(int flags, int devtype, proc_t p)
{
dprintf("tuntap: cdev_open()\n");
/* grab the lock so that there can only be one thread inside */
auto_lock l(&lock);
/* check wether it is already open */
if (open)
return EBUSY;
/* bring the network interface up */
int error = initialize_interface();
if (error)
return error;
open = true;
pid = proc_pid(p);
return 0;
}
int
tuntap_interface::cdev_close(int flags, int devtype, proc_t p)
{
dprintf("tuntap: cdev_close()\n");
auto_lock l(&lock);
if (open) {
open = false;
/* shut down the network interface */
shutdown_interface();
/* clear the queue */
send_queue.clear();
/* wakeup the cdev thread and notify selects */
wakeup(this);
selwakeup(&rsel);
return 0;
}
return EBADF;
}
int
tuntap_interface::cdev_read(uio_t uio, int ioflag)
{
auto_lock l(&lock);
unsigned int nb = 0;
int error;
dprintf("tuntap: cdev read\n");
if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP))
return EIO;
/* fetch a new mbuf from the queue if necessary */
mbuf_t cur_mbuf = NULL;
while (cur_mbuf == NULL) {
dprintf("tuntap: fetching new mbuf\n");
cur_mbuf = send_queue.dequeue();
if (cur_mbuf == NULL) {
/* nothing in queue, block or return */
if (!block_io) {
dprintf("tuntap: aborting (nbio)\n");
return EWOULDBLOCK;
} else {
/* block */
dprintf("tuntap: waiting\n");
/* release the lock while waiting */
l.unlock();
error = msleep(this, NULL, PZERO | PCATCH, "tuntap", NULL);
l.lock();
if (error)
return error;
/* see whether the device was closed in the meantime */
if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP))
return EIO;
}
}
}
/* notify bpf */
notify_bpf(cur_mbuf, true);
/* output what we have */
do {
dprintf("tuntap: got new mbuf: %p uio_resid: %d\n", cur_mbuf, uio_resid(uio));
/* now we have an mbuf */
int chunk_len = min(mbuf_len(cur_mbuf), uio_resid(uio));
error = uiomove((char *) mbuf_data(cur_mbuf), chunk_len, uio);
if (error) {
mbuf_freem(cur_mbuf);
return error;
}
nb += chunk_len;
dprintf("tuntap: moved %d bytes to userspace uio_resid: %d\n", chunk_len,
uio_resid(uio));
/* update cur_mbuf */
cur_mbuf = mbuf_free(cur_mbuf);
} while (uio_resid(uio) > 0 && cur_mbuf != NULL);
/* update statistics */
ifnet_stat_increment_out(ifp, 1, nb, 0);
/* still data left? forget about that ;-) */
if (cur_mbuf != NULL)
mbuf_freem(cur_mbuf);
dprintf("tuntap: read done\n");
return 0;
}
int
tuntap_interface::cdev_write(uio_t uio, int ioflag)
{
auto_lock l(&lock);
if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP))
return EIO;
dprintf("tuntap: cdev write. uio_resid: %d\n", uio_resid(uio));
/* pack the data into an mbuf chain */
mbuf_t first, mb;
/* first we need an mbuf having a header */
mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &first);
if (first == NULL) {
log(LOG_ERR, "tuntap: could not get mbuf.\n");
return ENOMEM;
}
mbuf_setlen(first, 0);
unsigned int mlen = mbuf_maxlen(first);
unsigned int chunk_len;
unsigned int copied = 0;
int error;
/* stuff the data into the mbuf(s) */
mb = first;
while (uio_resid(uio) > 0) {
/* copy a chunk. enforce mtu (don't know if this is correct behaviour) */
// ... evidently not :) -- Adam Ierymenko <adam.ierymenko@zerotier.com>
//chunk_len = min(ifnet_mtu(ifp), min(uio_resid(uio), mlen));
chunk_len = min(uio_resid(uio),mlen);
error = uiomove((caddr_t) mbuf_data(mb), chunk_len, uio);
if (error) {
log(LOG_ERR, "tuntap: could not copy data from userspace: %d\n", error);
mbuf_freem(first);
return error;
}
dprintf("tuntap: copied %d bytes, uio_resid %d\n", chunk_len,
uio_resid(uio));
mlen -= chunk_len;
mbuf_setlen(mb, mbuf_len(mb) + chunk_len);
copied += chunk_len;
/* if done, break the loop */
//if (uio_resid(uio) <= 0 || copied >= ifnet_mtu(ifp))
// break;
if (uio_resid(uio) <= 0)
break;
/* allocate a new mbuf if the current is filled */
if (mlen == 0) {
mbuf_t next;
mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &next);
if (next == NULL) {
log(LOG_ERR, "tuntap: could not get mbuf.\n");
mbuf_freem(first);
return ENOMEM;
}
mbuf_setnext(mb, next);
mb = next;
mbuf_setlen(mb, 0);
mlen = mbuf_maxlen(mb);
}
}
/* fill in header info */
mbuf_pkthdr_setrcvif(first, ifp);
mbuf_pkthdr_setlen(first, copied);
mbuf_pkthdr_setheader(first, mbuf_data(first));
mbuf_set_csum_performed(first, 0, 0);
/* update statistics */
ifnet_stat_increment_in(ifp, 1, copied, 0);
dprintf("tuntap: mbuf chain constructed. first: %p mb: %p len: %d data: %p\n",
first, mb, mbuf_len(first), mbuf_data(first));
/* notify bpf */
notify_bpf(first, false);
/* need to adjust the data pointer to point directly behind the linklevel header. The header
* itself is later accessed via m_pkthdr.header. Well, if something is ugly, here is it.
*/
mbuf_adj(first, ifnet_hdrlen(ifp));
/* pass the packet over to the network stack */
error = ifnet_input(ifp, first, NULL);
if (error) {
log(LOG_ERR, "tuntap: could not input packet into network stack.\n");
mbuf_freem(first);
return error;
}
return 0;
}
int
tuntap_interface::cdev_ioctl(u_long cmd, caddr_t data, int fflag, proc_t p)
{
auto_lock l(&lock);
dprintf("tuntap: cdev ioctl: %d\n", (int) (cmd & 0xff));
switch (cmd) {
case FIONBIO:
/* set i/o mode */
block_io = *((int *) data) ? false : true;
return 0;
case FIOASYNC:
/* don't allow switching it on */
if (*((int *) data))
return ENOTTY;
return 0;
}
return ENOTTY;
}
int
tuntap_interface::cdev_select(int which, void *wql, proc_t p)
{
auto_lock l(&lock);
int ret = 0;
dprintf("tuntap: select. which: %d\n", which);
switch (which) {
case FREAD:
/* check wether data is available */
{
if (!send_queue.empty())
ret = 1;
else {
dprintf("tuntap: select: waiting\n");
selrecord(p, &rsel, wql);
}
}
break;
case FWRITE:
/* we are always writeable */
ret = 1;
}
return ret;
}
/* interface service methods */
errno_t
tuntap_interface::if_output(mbuf_t m)
{
mbuf_t pkt;
dprintf("tuntap: if output\n");
/* just to be sure */
if (m == NULL)
return 0;
if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP)) {
mbuf_freem_list(m);
return EHOSTDOWN;
}
/* check whether packet has a header */
if ((mbuf_flags(m) & MBUF_PKTHDR) == 0) {
log(LOG_ERR, "tuntap: packet to be output has no mbuf header.\n");
mbuf_freem_list(m);
return EINVAL;
}
/* put the packet(s) into the output queue */
while (m != NULL) {
/* keep pointer, iterate */
pkt = m;
m = mbuf_nextpkt(m);
mbuf_setnextpkt(pkt, NULL);
auto_lock l(&lock);
if (!send_queue.enqueue(pkt)) {
mbuf_freem(pkt);
mbuf_freem_list(m);
return ENOBUFS;
}
}
/* protect the wakeup calls with the lock, not sure they are safe. */
{
auto_lock l(&lock);
/* wakeup the cdev thread and notify selects */
wakeup(this);
selwakeup(&rsel);
}
return 0;
}
errno_t
tuntap_interface::if_ioctl(u_int32_t cmd, void *arg)
{
dprintf("tuntap: if ioctl: %d\n", (int) (cmd & 0xff));
switch (cmd) {
case SIOCSIFADDR:
{
dprintf("tuntap: if_ioctl: SIOCSIFADDR\n");
/* Unfortunately, ifconfig sets the address family field of an INET
* netmask to zero, which makes early mDNSresponder versions ignore
* the interface. Fix that here. This one is of the category "ugly
* workaround". Dumb Darwin...
*
* Meanwhile, Apple has fixed mDNSResponder, and recent versions of
* Leopard don't need this hack anymore. However, Tiger still has a
* broken version so we leave the hack in for now.
*
* TODO: Revisit when dropping Tiger support.
*
* Btw. If you configure other network interfaces using ifconfig,
* you run into the same problem. I still don't know how to make the
* tap devices show up in the network configuration panel...
*/
ifaddr_t ifa = (ifaddr_t) arg;
if (ifa == NULL)
return 0;
sa_family_t af = ifaddr_address_family(ifa);
if (af != AF_INET)
return 0;
struct ifaliasreq ifra;
int sa_size = sizeof(struct sockaddr);
if (ifaddr_address(ifa, &ifra.ifra_addr, sa_size)
|| ifaddr_dstaddress(ifa, &ifra.ifra_broadaddr, sa_size)
|| ifaddr_netmask(ifa, &ifra.ifra_mask, sa_size)) {
log(LOG_WARNING,
"tuntap: failed to parse interface address.\n");
return 0;
}
// Check that the address family fields match. If not, issue another
// SIOCAIFADDR to fix the entry.
if (ifra.ifra_addr.sa_family != af
|| ifra.ifra_broadaddr.sa_family != af
|| ifra.ifra_mask.sa_family != af) {
log(LOG_INFO, "tuntap: Fixing address family for %s%d\n",
family_name, unit);
snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "%s%d",
family_name, unit);
ifra.ifra_addr.sa_family = af;
ifra.ifra_broadaddr.sa_family = af;
ifra.ifra_mask.sa_family = af;
do_sock_ioctl(af, SIOCAIFADDR, &ifra);
}
return 0;
}
case SIOCSIFFLAGS:
return 0;
case SIOCGIFSTATUS:
{
struct ifstat *stat = (struct ifstat *) arg;
int len;
char *p;
if (stat == NULL)
return EINVAL;
/* print status */
len = strlen(stat->ascii);
p = stat->ascii + len;
if (open) {
snprintf(p, IFSTATMAX - len, "\topen (pid %u)\n", pid);
} else {
snprintf(p, IFSTATMAX - len, "\tclosed\n");
}
return 0;
}
case SIOCSIFMTU:
{
struct ifreq *ifr = (struct ifreq *) arg;
if (ifr == NULL)
return EINVAL;
ifnet_set_mtu(ifp, ifr->ifr_mtu);
return 0;
}
case SIOCDIFADDR:
return 0;
}
return EOPNOTSUPP;
}
errno_t
tuntap_interface::if_set_bpf_tap(bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t))
{
dprintf("tuntap: mode %d\n", mode);
auto_lock l(&bpf_lock);
bpf_callback = cb;
bpf_mode = mode;
return 0;
}
errno_t
tuntap_interface::if_check_multi(const struct sockaddr *maddr)
{
dprintf("tuntap: if_check_multi\n");
return EOPNOTSUPP;
}
void
tuntap_interface::if_detached()
{
dprintf("tuntap: if_detached\n");
/* wake unregister_interface() */
detach_lock.lock();
interface_detached = true;
detach_lock.wakeup(&interface_detached);
detach_lock.unlock();
dprintf("if_detached done\n");
}