Add support for iPhones and recent iPods.
authorGuus Sliepen <guus@tinc-vpn.org>
Thu, 10 Sep 2009 17:32:54 +0000 (19:32 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Thu, 10 Sep 2009 17:32:54 +0000 (19:32 +0200)
This is a slightly modified patch from Grzegorz Dymarek that allows tinc to use
the tunemu device, which allows tinc to be compiled for iPhones and recent
iPods. To enable support for tunemu, the --enable-tunemu option has to be used
when running the configure script.

THANKS
configure.in
src/Makefile.am
src/bsd/device.c
src/bsd/tunemu.c [new file with mode: 0644]
src/bsd/tunemu.h [new file with mode: 0644]

diff --git a/THANKS b/THANKS
index 2daa8f1651b07de4c5cdb2c3514a7d050bd99e53..ed57da59cd8e1ab2266c6c48cd98da45f832379e 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -8,6 +8,7 @@ We would like to thank the following people for their contributions to tinc:
 * Cris van Pelt
 * Enrique Zanardi
 * Flynn Marquardt
+* Grzegorz Dymarek
 * Hans Bayle
 * Ivo van Dong
 * James MacLean
index 964dd27a969d45ec1dc6172499ee5a72c3d28ea7..eff93a0881a59be8bda98d9f18453a0347f4f8b3 100644 (file)
@@ -75,6 +75,15 @@ case $host_os in
   ;;
 esac
 
+AC_ARG_ENABLE(tunemu,
+  AS_HELP_STRING([--enable-tunemu], [enable support for the tunemu driver]),
+  [ AC_DEFINE(ENABLE_TUNEMU, 1, [Support for tunemu])
+    tunemu=true
+  ]
+)
+
+AM_CONDITIONAL(TUNEMU, test "$tunemu" = true)
+
 AC_CACHE_SAVE
 
 if test -d /sw/include ; then
index 10f07af59e950e86de7acf717813d9c4cdaab6c1..501fdf66953875873d88289e60dc36ea26308961 100644 (file)
@@ -9,6 +9,10 @@ tincd_SOURCES = conf.c connection.c edge.c event.c graph.c logger.c meta.c net.c
        net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c       \
        protocol_key.c protocol_subnet.c route.c subnet.c tincd.c
 
+if TUNEMU
+tincd_SOURCES += bsd/tunemu.c
+endif
+
 nodist_tincd_SOURCES = device.c
 
 DEFAULT_INCLUDES =
@@ -16,10 +20,14 @@ DEFAULT_INCLUDES =
 INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib
 
 noinst_HEADERS = conf.h connection.h device.h edge.h event.h graph.h logger.h meta.h net.h netutl.h node.h process.h   \
-       protocol.h route.h subnet.h
+       protocol.h route.h subnet.h bsd/tunemu.h
 
 LIBS = @LIBS@ @LIBINTL@
 
+if TUNEMU
+LIBS += -lpcap
+endif
+
 tincd_LDADD = \
        $(top_builddir)/lib/libvpn.a
 
index 2e8908af3dce5e54baa24e25e303fbe6026b2dfc..fe85d10fc534a166244457dbd01e0512b81c8afb 100644 (file)
 #include "utils.h"
 #include "xalloc.h"
 
+#ifdef HAVE_TUNEMU
+#include "bsd/tunemu.h"
+#endif
+
 #define DEFAULT_DEVICE "/dev/tun0"
 
 typedef enum device_type {
        DEVICE_TYPE_TUN,
        DEVICE_TYPE_TUNIFHEAD,
        DEVICE_TYPE_TAP,
+#ifdef HAVE_TUNEMU
+       DEVICE_TYPE_TUNEMU,
+#endif
 } device_type_t;
 
 int device_fd = -1;
@@ -43,7 +50,9 @@ char *iface = NULL;
 static char *device_info = NULL;
 static int device_total_in = 0;
 static int device_total_out = 0;
-#if defined(HAVE_OPENBSD) || defined(HAVE_FREEBSD)
+#if defined(TUNEMU)
+static device_type_t device_type = DEVICE_TYPE_TUNEMU;
+#elif defined(HAVE_OPENBSD) || defined(HAVE_FREEBSD)
 static device_type_t device_type = DEVICE_TYPE_TUNIFHEAD;
 #else
 static device_type_t device_type = DEVICE_TYPE_TUN;
@@ -60,14 +69,13 @@ bool setup_device(void) {
        if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
                iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device);
 
-       if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0) {
-               logger(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno));
-               return false;
-       }
-
        if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) {
                if(!strcasecmp(type, "tun"))
                        /* use default */;      
+#ifdef HAVE_TUNEMU
+               else if(!strcasecmp(type, "tunemu"))
+                       device_type = DEVICE_TYPE_TUNEMU;
+#endif
                else if(!strcasecmp(type, "tunnohead"))
                        device_type = DEVICE_TYPE_TUN;
                else if(!strcasecmp(type, "tunifhead"))
@@ -83,6 +91,23 @@ bool setup_device(void) {
                        device_type = DEVICE_TYPE_TAP;
        }
 
+       switch(device_type) {
+#ifdef HAVE_TUNEMU
+               case DEVICE_TYPE_TUNEMU: {
+                       char dynamic_name[256] = "";
+                       device_fd = tunemu_open(dynamic_name);
+               }
+                       break;
+#endif
+               default:
+                       device_fd = open(device, O_RDWR | O_NONBLOCK);
+       }
+
+       if(device_fd < 0) {
+               logger(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno));
+               return false;
+       }
+
        switch(device_type) {
                default:
                        device_type = DEVICE_TYPE_TUN;
@@ -129,6 +154,11 @@ bool setup_device(void) {
                                overwrite_mac = true;
                        device_info = _("Generic BSD tap device");
                        break;
+#ifdef HAVE_TUNEMU
+               case DEVICE_TYPE_TUNEMU:
+                       device_info = _("BSD tunemu device");
+                       break;
+#endif
        }
 
        logger(LOG_INFO, _("%s is a %s"), device, device_info);
@@ -139,7 +169,15 @@ bool setup_device(void) {
 void close_device(void) {
        cp();
 
-       close(device_fd);
+       switch(device_type) {
+#ifdef HAVE_TUNEMU
+               case DEVICE_TYPE_TUNEMU:
+                       tunemu_close(device_fd);
+                       break;
+#endif
+               default:
+                       close(device_fd);
+       }
 
        free(device);
        free(iface);
@@ -152,7 +190,16 @@ bool read_packet(vpn_packet_t *packet) {
 
        switch(device_type) {
                case DEVICE_TYPE_TUN:
-                       if((lenin = read(device_fd, packet->data + 14, MTU - 14)) <= 0) {
+#ifdef HAVE_TUNEMU
+               case DEVICE_TYPE_TUNEMU:
+                       if(device_type == DEVICE_TYPE_TUNEMU)
+                               lenin = tunemu_read(device_fd, packet->data + 14, MTU - 14);
+                       else
+#else
+                               lenin = read(device_fd, packet->data + 14, MTU - 14);
+#endif
+
+                       if(lenin <= 0) {
                                logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info,
                                           device, strerror(errno));
                                return false;
@@ -228,6 +275,7 @@ bool read_packet(vpn_packet_t *packet) {
        ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Read packet of %d bytes from %s"),
                           packet->len, device_info);
 
+       logger(LOG_INFO, "E:fd_read");
        return true;
 }
 
@@ -284,6 +332,16 @@ bool write_packet(vpn_packet_t *packet)
                        }
                        break;
 
+#ifdef HAVE_TUNEMU
+               case DEVICE_TYPE_TUNEMU:
+                       if(tunemu_write(device_fd, packet->data + 14, packet->len - 14) < 0) {
+                               logger(LOG_ERR, _("Error while writing to %s %s: %s"), device_info,
+                                          device, strerror(errno));
+                               return false;
+                       }
+                       break;
+#endif
+
                default:
                        return false;
        }
diff --git a/src/bsd/tunemu.c b/src/bsd/tunemu.c
new file mode 100644 (file)
index 0000000..f532b04
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ *  tunemu - Tun device emulation for Darwin
+ *  Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
+ *  
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *  
+ */
+
+#include "tunemu.h"
+
+#include <sys/socket.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <util.h>
+#include <pcap.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#define PPPPROTO_CTL 1
+
+#define PPP_IP         0x21
+#define PPP_IPV6       0x57
+
+#define SC_LOOP_TRAFFIC 0x00000200
+
+#define PPPIOCNEWUNIT  _IOWR('t', 62, int)
+#define        PPPIOCSFLAGS    _IOW('t', 89, int)
+#define PPPIOCSNPMODE  _IOW('t', 75, struct npioctl)
+#define PPPIOCATTCHAN  _IOW('t', 56, int)
+#define PPPIOCGCHAN            _IOR('t', 55, int)
+#define PPPIOCCONNECT  _IOW('t', 58, int)
+#define        PPPIOCGUNIT             _IOR('t', 86, int)
+
+struct sockaddr_ppp
+{
+       u_int8_t ppp_len;
+       u_int8_t ppp_family;
+       u_int16_t ppp_proto;
+       u_int32_t ppp_cookie;
+};
+
+enum NPmode
+{
+       NPMODE_PASS,
+    NPMODE_DROP,
+    NPMODE_ERROR,
+    NPMODE_QUEUE
+};
+
+struct npioctl
+{
+       int protocol;
+       enum NPmode mode;
+};
+
+#define PPP_KEXT_PATH "/System/Library/Extensions/PPP.kext"
+
+#define ERROR_BUFFER_SIZE 1024
+
+char tunemu_error[ERROR_BUFFER_SIZE];
+
+static int pcap_use_count = 0;
+static pcap_t *pcap = NULL;
+
+static int data_buffer_length = 0;
+static char *data_buffer = NULL;
+
+static void tun_error(char *format, ...)
+{
+       va_list vl;
+       va_start(vl, format);
+       vsnprintf(tunemu_error, ERROR_BUFFER_SIZE, format, vl);
+       va_end(vl);
+}
+
+static void tun_noerror()
+{
+       *tunemu_error = 0;
+}
+
+static void closeall()
+{
+    int fd = getdtablesize();
+       while (fd--)
+               close(fd);
+
+    open("/dev/null", O_RDWR, 0);
+    dup(0);
+    dup(0);
+}
+
+static int ppp_load_kext()
+{
+       int pid = fork();
+       if (pid < 0)
+       {
+               tun_error("fork for ppp kext: %s", strerror(errno));
+               return -1;
+       }
+
+       if (pid == 0)
+       {
+               closeall();
+               execle("/sbin/kextload", "kextload", PPP_KEXT_PATH, NULL, NULL);
+               exit(1);
+       }
+
+       int status;
+       while (waitpid(pid, &status, 0) < 0)
+       {
+               if (errno == EINTR)
+                       continue;
+
+               tun_error("waitpid for ppp kext: %s", strerror(errno));
+               return -1;
+       }
+
+       if (WEXITSTATUS(status) != 0)
+       {
+               tun_error("could not load ppp kext \"%s\"", PPP_KEXT_PATH);
+               return -1;
+       }
+
+       tun_noerror();
+       return 0;
+}
+
+static int ppp_new_instance()
+{
+       // create ppp socket
+    int ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
+    if (ppp_sockfd < 0)
+       {
+               if (ppp_load_kext() < 0)
+                       return -1;
+
+               ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
+               if (ppp_sockfd < 0)
+               {
+                       tun_error("creating ppp socket: %s", strerror(errno));
+                       return -1;
+               }
+       }
+
+       // connect to ppp procotol
+    struct sockaddr_ppp pppaddr;
+    pppaddr.ppp_len = sizeof(struct sockaddr_ppp);
+    pppaddr.ppp_family = AF_PPP;
+    pppaddr.ppp_proto = PPPPROTO_CTL;
+    pppaddr.ppp_cookie = 0;
+    if (connect(ppp_sockfd, (struct sockaddr *)&pppaddr, sizeof(struct sockaddr_ppp)) < 0)
+       {
+               tun_error("connecting ppp socket: %s", strerror(errno));
+               close(ppp_sockfd);
+               return -1;
+    }
+
+       tun_noerror();
+       return ppp_sockfd;
+}
+
+static int ppp_new_unit(int *unit_number)
+{
+       int fd = ppp_new_instance();
+       if (fd < 0)
+               return -1;
+
+       // create ppp unit
+       if (ioctl(fd, PPPIOCNEWUNIT, unit_number) < 0)
+       {
+               tun_error("creating ppp unit: %s", strerror(errno));
+               close(fd);
+               return -1;
+    }
+
+       tun_noerror();
+       return fd;
+}
+
+static int ppp_setup_unit(int unit_fd)
+{
+       // send traffic to program
+       int flags = SC_LOOP_TRAFFIC;
+       if (ioctl(unit_fd, PPPIOCSFLAGS, &flags) < 0)
+       {
+               tun_error("setting ppp loopback mode: %s", strerror(errno));
+               return -1;
+    }
+
+       // allow packets
+       struct npioctl npi;
+       npi.protocol = PPP_IP;
+       npi.mode = NPMODE_PASS;
+       if (ioctl(unit_fd, PPPIOCSNPMODE, &npi) < 0)
+       {
+               tun_error("starting ppp unit: %s", strerror(errno));
+               return -1;
+       }
+
+       tun_noerror();
+       return 0;
+}
+
+static int open_pcap()
+{
+       if (pcap != NULL)
+       {
+               pcap_use_count++;
+               return 0;
+       }
+
+       char errbuf[PCAP_ERRBUF_SIZE];
+       pcap = pcap_open_live("lo0", BUFSIZ, 0, 1, errbuf);
+       pcap_use_count = 1;
+
+       if (pcap == NULL)
+       {
+               tun_error("opening pcap: %s", errbuf);
+               return -1;
+       }
+
+       tun_noerror();
+       return 0;
+}
+
+static void close_pcap()
+{
+       if (pcap == NULL)
+               return;
+
+       pcap_use_count--;
+       if (pcap_use_count == 0)
+       {
+               pcap_close(pcap);
+               pcap = NULL;
+       }
+}
+
+static void allocate_data_buffer(int size)
+{
+       if (data_buffer_length < size)
+       {
+               free(data_buffer);
+               data_buffer_length = size;
+               data_buffer = malloc(data_buffer_length);
+       }
+}
+
+static void make_device_name(tunemu_device device, int unit_number)
+{
+       snprintf(device, sizeof(tunemu_device), "ppp%d", unit_number);
+}
+
+static int check_device_name(tunemu_device device)
+{
+       if (strlen(device) < 4)
+               return -1;
+
+       int unit_number = atoi(device + 3);
+       if (unit_number < 0 || unit_number > 999)
+               return -1;
+
+       tunemu_device compare;
+       make_device_name(compare, unit_number);
+
+       if (strcmp(device, compare) != 0)
+               return -1;
+
+       return 0;
+}
+
+int tunemu_open(tunemu_device device)
+{
+       int ppp_unit_number = -1;
+       if (device[0] != 0)
+       {
+               if (check_device_name(device) < 0)
+               {
+                       tun_error("invalid device name \"%s\"", device);
+                       return -1;
+               }
+
+               ppp_unit_number = atoi(device + 3);
+       }
+
+       int ppp_unit_fd = ppp_new_unit(&ppp_unit_number);
+       if (ppp_unit_fd < 0)
+               return -1;
+
+       if (ppp_setup_unit(ppp_unit_fd) < 0)
+       {
+               close(ppp_unit_fd);
+               return -1;
+       }
+
+       if (open_pcap() < 0)
+       {
+               close(ppp_unit_fd);
+               return -1;
+       }
+
+       make_device_name(device, ppp_unit_number);
+
+       return ppp_unit_fd;
+}
+
+int tunemu_close(int ppp_sockfd)
+{
+       int ret = close(ppp_sockfd);
+
+       if (ret == 0)
+               close_pcap();
+
+       return ret;
+}
+
+int tunemu_read(int ppp_sockfd, char *buffer, int length)
+{
+       allocate_data_buffer(length + 2);
+
+       length = read(ppp_sockfd, data_buffer, length + 2);
+       if (length < 0)
+       {
+               tun_error("reading packet: %s", strerror(errno));
+               return length;
+       }
+       tun_noerror();
+
+       length -= 2;
+       if (length < 0)
+               return 0;
+
+       memcpy(buffer, data_buffer + 2, length);
+
+       return length;
+}
+
+int tunemu_write(int ppp_sockfd, char *buffer, int length)
+{
+       allocate_data_buffer(length + 4);
+
+       data_buffer[0] = 0x02;
+       data_buffer[1] = 0x00;
+       data_buffer[2] = 0x00;
+       data_buffer[3] = 0x00;
+
+       memcpy(data_buffer + 4, buffer, length);
+
+       if (pcap == NULL)
+       {
+               tun_error("pcap not open");
+               return -1;
+       }
+
+       length = pcap_inject(pcap, data_buffer, length + 4);
+       if (length < 0)
+       {
+               tun_error("injecting packet: %s", pcap_geterr(pcap));
+               return length;
+       }
+       tun_noerror();
+
+       length -= 4;
+       if (length < 0)
+               return 0;
+
+       return length;
+}
diff --git a/src/bsd/tunemu.h b/src/bsd/tunemu.h
new file mode 100644 (file)
index 0000000..42b1785
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *  tunemu - Tun device emulation for Darwin
+ *  Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
+ *  
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *  
+ */
+
+#ifndef TUNEMU_H
+#define TUNEMU_H
+
+typedef char tunemu_device[7];
+
+extern char tunemu_error[];
+
+int tunemu_open(tunemu_device dev);
+int tunemu_close(int fd);
+int tunemu_read(int fd, char *buffer, int length);
+int tunemu_write(int fd, char *buffer, int length);
+
+#endif