summaryrefslogtreecommitdiff
path: root/tap-mac/tuntap/src/tuntap.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tap-mac/tuntap/src/tuntap.cc')
-rw-r--r--tap-mac/tuntap/src/tuntap.cc962
1 files changed, 962 insertions, 0 deletions
diff --git a/tap-mac/tuntap/src/tuntap.cc b/tap-mac/tuntap/src/tuntap.cc
new file mode 100644
index 00000000..941de94c
--- /dev/null
+++ b/tap-mac/tuntap/src/tuntap.cc
@@ -0,0 +1,962 @@
+/*
+ * 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) */
+ chunk_len = min(ifnet_mtu(ifp), 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;
+
+ /* 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");
+}
+