diff options
Diffstat (limited to 'ext/tap-mac/tuntap/src/tap/tap.cc')
-rw-r--r-- | ext/tap-mac/tuntap/src/tap/tap.cc | 533 |
1 files changed, 0 insertions, 533 deletions
diff --git a/ext/tap-mac/tuntap/src/tap/tap.cc b/ext/tap-mac/tuntap/src/tap/tap.cc deleted file mode 100644 index b348a85e..00000000 --- a/ext/tap-mac/tuntap/src/tap/tap.cc +++ /dev/null @@ -1,533 +0,0 @@ -/* - * ethertap device for macosx. - * - * tap_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 "tap.h" - -extern "C" { - -#include <sys/systm.h> -#include <sys/syslog.h> -#include <sys/param.h> -#include <sys/sockio.h> -#include <sys/random.h> -#include <sys/kern_event.h> - -#include <mach/thread_policy.h> - -#include <net/if_types.h> -#include <net/if_arp.h> -#include <net/if_dl.h> -#include <net/if_media.h> -#include <net/dlil.h> -#include <net/ethernet.h> - -} - -#if 0 -#define dprintf(...) log(LOG_INFO, __VA_ARGS__) -#else -#define dprintf(...) -#endif - -// These declarations are missing in the Kernel.framework headers, put present in userspace :-/ -#pragma pack(4) -struct ifmediareq { - char ifm_name[IFNAMSIZ]; /* if name, e.g. "en0" */ - int ifm_current; /* current media options */ - int ifm_mask; /* don't care mask */ - int ifm_status; /* media status */ - int ifm_active; /* active options */ - int ifm_count; /* # entries in ifm_ulist array */ - int *ifm_ulist; /* media words */ -}; - -struct ifmediareq64 { - char ifm_name[IFNAMSIZ]; /* if name, e.g. "en0" */ - int ifm_current; /* current media options */ - int ifm_mask; /* don't care mask */ - int ifm_status; /* media status */ - int ifm_active; /* active options */ - int ifm_count; /* # entries in ifm_ulist array */ - user64_addr_t ifmu_ulist __attribute__((aligned(8))); -}; - -struct ifmediareq32 { - char ifm_name[IFNAMSIZ]; /* if name, e.g. "en0" */ - int ifm_current; /* current media options */ - int ifm_mask; /* don't care mask */ - int ifm_status; /* media status */ - int ifm_active; /* active options */ - int ifm_count; /* # entries in ifm_ulist array */ - user32_addr_t ifmu_ulist; /* 32-bit pointer */ -}; -#pragma pack() - -#define SIOCGIFMEDIA32 _IOWR('i', 56, struct ifmediareq32) /* get net media */ -#define SIOCGIFMEDIA64 _IOWR('i', 56, struct ifmediareq64) /* get net media (64-bit) */ - -/* thread_policy_set is exported in Mach.kext, but commented in mach/thread_policy.h in the - * Kernel.Framework headers (why?). Add a local declaration to work around that. - */ -extern "C" { -kern_return_t thread_policy_set( - thread_t thread, - thread_policy_flavor_t flavor, - thread_policy_t policy_info, - mach_msg_type_number_t count); -} - -static unsigned char ETHER_BROADCAST_ADDR[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - -/* members */ -tap_interface::tap_interface() { - bzero(attached_protos, sizeof(attached_protos)); - input_thread = THREAD_NULL; -} - -bool -tap_interface::initialize(unsigned short major, unsigned short unit) -{ - this->unit = unit; - this->family_name = TAP_FAMILY_NAME; - this->family = IFNET_FAMILY_ETHERNET; - this->type = IFT_ETHER; - bzero(unique_id, UIDLEN); - snprintf(unique_id, UIDLEN, "%s%d", family_name, unit); - - dprintf("tap: starting interface %s%d\n", TAP_FAMILY_NAME, unit); - - /* register character device */ - if (!tuntap_interface::register_chardev(major)) - return false; - - return true; -} - -void -tap_interface::shutdown() -{ - dprintf("tap: shutting down tap interface %s%d\n", TAP_FAMILY_NAME, unit); - - unregister_chardev(); -} - -int -tap_interface::initialize_interface() -{ - struct sockaddr_dl lladdr; - lladdr.sdl_len = sizeof(lladdr); - lladdr.sdl_family = AF_LINK; - lladdr.sdl_alen = ETHER_ADDR_LEN; - lladdr.sdl_nlen = lladdr.sdl_slen = 0; - - /* generate a random MAC address */ - read_random(LLADDR(&lladdr), ETHER_ADDR_LEN); - - /* clear multicast bit and set local assignment bit (see IEEE 802) */ - (LLADDR(&lladdr))[0] &= 0xfe; - (LLADDR(&lladdr))[0] |= 0x02; - - dprintf("tap: random tap address: %02x:%02x:%02x:%02x:%02x:%02x\n", - (LLADDR(&lladdr))[0] & 0xff, - (LLADDR(&lladdr))[1] & 0xff, - (LLADDR(&lladdr))[2] & 0xff, - (LLADDR(&lladdr))[3] & 0xff, - (LLADDR(&lladdr))[4] & 0xff, - (LLADDR(&lladdr))[5] & 0xff); - - /* register interface */ - if (!tuntap_interface::register_interface(&lladdr, ETHER_BROADCAST_ADDR, ETHER_ADDR_LEN)) - return EIO; - - /* Set link level address. Yes, we need to do that again. Darwin sucks. */ - errno_t err = ifnet_set_lladdr(ifp, LLADDR(&lladdr), ETHER_ADDR_LEN); - if (err) - dprintf("tap: failed to set lladdr on %s%d: %d\n", family_name, unit, err); - - /* set mtu */ - ifnet_set_mtu(ifp, TAP_MTU); - /* set header length */ - ifnet_set_hdrlen(ifp, sizeof(struct ether_header)); - /* add the broadcast flag */ - ifnet_set_flags(ifp, IFF_BROADCAST, IFF_BROADCAST); - - /* we must call bpfattach(). Otherwise we deadlock BPF while unloading. Seems to be a bug in - * the kernel, see bpfdetach() in net/bpf.c, it will return without releasing the lock if - * the interface wasn't attached. I wonder what they were smoking while writing it ;-) - */ - bpfattach(ifp, DLT_EN10MB, ifnet_hdrlen(ifp)); - - /* Inject an empty packet to trigger the input thread calling demux(), which will unblock - * thread_sync_lock. This is part of a hack to avoid a kernel crash on re-attaching - * interfaces, see comment in shutdown_interface for more information. - */ - mbuf_t empty_mbuf; - mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &empty_mbuf); - if (empty_mbuf != NULL) { - mbuf_pkthdr_setrcvif(empty_mbuf, ifp); - mbuf_pkthdr_setlen(empty_mbuf, 0); - mbuf_pkthdr_setheader(empty_mbuf, mbuf_data(empty_mbuf)); - mbuf_set_csum_performed(empty_mbuf, 0, 0); - if (ifnet_input(ifp, empty_mbuf, NULL) == 0) { - auto_lock l(&thread_sync_lock); - for (int i = 0; i < 100 && input_thread == THREAD_NULL; ++i) { - dprintf("input thread not found, waiting...\n"); - thread_sync_lock.sleep(&input_thread, 10000000); - } - } else { - mbuf_freem(empty_mbuf); - } - } - if (input_thread == THREAD_NULL) - dprintf("Failed to determine input thread!\n"); - - return 0; -} - -void -tap_interface::shutdown_interface() -{ - dprintf("tap: shutting down network interface of device %s%d\n", TAP_FAMILY_NAME, unit); - - /* detach all protocols */ - for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { - if (attached_protos[i].used) { - errno_t err = ifnet_detach_protocol(ifp, attached_protos[i].proto); - if (err) - log(LOG_WARNING, "tap: could not detach protocol %d from %s%d\n", - attached_protos[i].proto, TAP_FAMILY_NAME, unit); - } - } - - cleanup_interface(); - unregister_interface(); - - /* There's a race condition in the kernel that may cause crashes when quickly re-attaching - * interfaces. The crash happens when the interface gets re-attached before the input thread - * for the interface managed to terminate, in which case an assert on the input_waiting flag - * to be clear triggers in ifnet_attach. The bug is really that there's no synchronization - * for terminating the input thread. To work around this, the following code does add the - * missing synchronization to wait for the input thread to terminate. Of course, threading - * primitives available to kexts are few, and I'm not aware of a way to wait for a thread to - * terminate. Hence, the code calls thread_policy_set (passing bogus parameters) in a loop, - * until it returns KERN_TERMINATED. Since this is all rather fragile, there's an upper - * limit on the loop iteratations we're willing to make, so this terminates eventually even - * if things change on the kernel side eventually. - */ - if (input_thread != THREAD_NULL) { - dprintf("Waiting for input thread...\n"); - kern_return_t result = 0; - for (int i = 0; i < 100; ++i) { - result = thread_policy_set(input_thread, -1, NULL, 0); - dprintf("thread_policy_set result: %d\n", result); - if (result == KERN_TERMINATED) { - dprintf("Input thread terminated.\n"); - thread_deallocate(input_thread); - input_thread = THREAD_NULL; - break; - } - - auto_lock l(&thread_sync_lock); - thread_sync_lock.sleep(&input_thread, 10000000); - } - } -} - -errno_t -tap_interface::if_ioctl(u_int32_t cmd, void *arg) -{ - dprintf("tap: if_ioctl cmd: %d (%x)\n", cmd & 0xff, cmd); - - switch (cmd) { - case SIOCSIFLLADDR: - { - /* set ethernet address */ - struct sockaddr *ea = &(((struct ifreq *) arg)->ifr_addr); - - dprintf("tap: SIOCSIFLLADDR family %d len %d\n", - ea->sa_family, ea->sa_len); - - /* check if it is really an ethernet address */ - if (ea->sa_family != AF_LINK || ea->sa_len != ETHER_ADDR_LEN) - return EINVAL; - - /* ok, copy */ - errno_t err = ifnet_set_lladdr(ifp, ea->sa_data, ETHER_ADDR_LEN); - if (err) { - dprintf("tap: failed to set lladdr on %s%d: %d\n", - family_name, unit, err); - return err; - } - - /* Generate a LINK_ON event. This necessary for configd to re-read - * the interface data and refresh the MAC address. Not doing so - * would result in the DHCP client using a stale MAC address... - */ - generate_link_event(KEV_DL_LINK_ON); - - return 0; - } - - case SIOCGIFMEDIA32: - case SIOCGIFMEDIA64: - { - struct ifmediareq *ifmr = (struct ifmediareq*) arg; - user_addr_t list = USER_ADDR_NULL; - - ifmr->ifm_current = IFM_ETHER; - ifmr->ifm_mask = 0; - ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; - ifmr->ifm_active = IFM_ETHER; - ifmr->ifm_count = 1; - - if (cmd == SIOCGIFMEDIA64) - list = ((struct ifmediareq64*) ifmr)->ifmu_ulist; - else - list = CAST_USER_ADDR_T( - ((struct ifmediareq32*) ifmr)->ifmu_ulist); - - if (list != USER_ADDR_NULL) - return copyout(&ifmr->ifm_current, list, sizeof(int)); - - return 0; - } - - default: - /* let our superclass handle it */ - return tuntap_interface::if_ioctl(cmd, arg); - } - - return EOPNOTSUPP; -} - -errno_t -tap_interface::if_demux(mbuf_t m, char *header, protocol_family_t *proto) -{ - struct ether_header *eh = (struct ether_header *) header; - unsigned char lladdr[ETHER_ADDR_LEN]; - - dprintf("tap: if_demux\n"); - - /* Make note of what input thread this interface is running on. This is part of a hack to - * avoid a crash on re-attaching interfaces, see comment in shutdown_interface for details. - */ - if (input_thread == THREAD_NULL) { - auto_lock l(&thread_sync_lock); - input_thread = current_thread(); - thread_reference(input_thread); - thread_sync_lock.wakeup(&input_thread); - } - - /* size check */ - if (mbuf_len(m) < sizeof(struct ether_header)) - return ENOENT; - - /* catch broadcast and multicast (stolen from bsd/net/ether_if_module.c) */ - if (eh->ether_dhost[0] & 1) { - if (memcmp(ETHER_BROADCAST_ADDR, eh->ether_dhost, ETHER_ADDR_LEN) == 0) { - /* broadcast */ - dprintf("tap: broadcast packet.\n"); - mbuf_setflags_mask(m, MBUF_BCAST, MBUF_BCAST); - } else { - /* multicast */ - dprintf("tap: multicast packet.\n"); - mbuf_setflags_mask(m, MBUF_MCAST, MBUF_MCAST); - } - } else { - /* check wether the packet has our address */ - ifnet_lladdr_copy_bytes(ifp, lladdr, ETHER_ADDR_LEN); - if (memcmp(lladdr, eh->ether_dhost, ETHER_ADDR_LEN) != 0) - mbuf_setflags_mask(m, MBUF_PROMISC, MBUF_PROMISC); - } - - /* find the protocol */ - for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { - if (attached_protos[i].used && attached_protos[i].type == eh->ether_type) { - *proto = attached_protos[i].proto; - return 0; - } - } - - dprintf("tap: if_demux() failed to find proto.\n"); - - /* no matching proto found */ - return ENOENT; -} - -errno_t -tap_interface::if_framer(mbuf_t *m, const struct sockaddr *dest, const char *dest_linkaddr, - const char *frame_type) -{ - struct ether_header *eh; - mbuf_t nm = *m; - errno_t err; - - dprintf("tap: if_framer\n"); - - /* prepend the ethernet header */ - err = mbuf_prepend(&nm, sizeof (struct ether_header), MBUF_WAITOK); - if (err) { - dprintf("tap: could not prepend data to mbuf: %d\n", err); - return err; - } - *m = nm; - - /* fill the header */ - eh = (struct ether_header *) mbuf_data(*m); - memcpy(eh->ether_dhost, dest_linkaddr, ETHER_ADDR_LEN); - ifnet_lladdr_copy_bytes(ifp, eh->ether_shost, ETHER_ADDR_LEN); - eh->ether_type = *((u_int16_t *) frame_type); - - return 0; -} - -errno_t -tap_interface::if_add_proto(protocol_family_t proto, const struct ifnet_demux_desc *desc, - u_int32_t ndesc) -{ - errno_t err; - - dprintf("tap: if_add_proto proto %d\n", proto); - - for (unsigned int i = 0; i < ndesc; i++) { - /* try to add the protocol */ - err = add_one_proto(proto, desc[i]); - if (err != 0) { - /* if that fails, remove everything stored so far */ - if_del_proto(proto); - return err; - } - } - - return 0; -} - -errno_t -tap_interface::if_del_proto(protocol_family_t proto) -{ - dprintf("tap: if_del_proto proto %d\n", proto); - - /* delete all matching entries in attached_protos */ - for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { - if (attached_protos[i].proto == proto) - attached_protos[i].used = false; - } - - return 0; -} - -errno_t -tap_interface::if_check_multi(const struct sockaddr *maddr) -{ - dprintf("tap: if_check_multi family %d\n", maddr->sa_family); - - /* see whether it is a ethernet address with the multicast bit set */ - if (maddr->sa_family == AF_LINK) { - struct sockaddr_dl *dlmaddr = (struct sockaddr_dl *) maddr; - if (LLADDR(dlmaddr)[0] & 0x01) - return 0; - else - return EADDRNOTAVAIL; - } - - return EOPNOTSUPP; -} - -errno_t -tap_interface::add_one_proto(protocol_family_t proto, const struct ifnet_demux_desc &dd) -{ - int free = -1; - u_int16_t dt; - - /* we only support DLIL_DESC_ETYPE2 */ - if (dd.type != DLIL_DESC_ETYPE2 || dd.datalen != 2) { - log(LOG_WARNING, "tap: tap only supports DLIL_DESC_ETYPE2 protocols.\n"); - return EINVAL; - } - - dt = *((u_int16_t *) (dd.data)); - - /* see if the protocol is already registered */ - for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { - if (attached_protos[i].used) { - if (dt == attached_protos[i].type) { - /* already registered */ - if (attached_protos[i].proto == proto) { - /* matches the old entry */ - return 0; - } else - return EEXIST; - } - } else if (free == -1) - free = i; - } - - /* did we find a free entry? */ - if (free == -1) - /* is ENOBUFS correct? */ - return ENOBUFS; - - /* ok, save information */ - attached_protos[free].used = true; - attached_protos[free].type = dt; - attached_protos[free].proto = proto; - - return 0; -} - -/* This code is shamelessly stolen from if_bond.c */ -void -tap_interface::generate_link_event(u_int32_t code) -{ - struct { - struct kern_event_msg header; - u_int32_t unit; - char if_name[IFNAMSIZ]; - } event; - - bzero(&event, sizeof(event)); - event.header.total_size = sizeof(event); - event.header.vendor_code = KEV_VENDOR_APPLE; - event.header.kev_class = KEV_NETWORK_CLASS; - event.header.kev_subclass = KEV_DL_SUBCLASS; - event.header.event_code = code; - event.header.event_data[0] = family; - event.unit = (u_int32_t) unit; - strncpy(event.if_name, ifnet_name(ifp), IFNAMSIZ); - - ifnet_event(ifp, &event.header); -} - -/* tap_manager members */ -tuntap_interface * -tap_manager::create_interface() -{ - return new tap_interface(); -} - |