From 4a5d42178cc0954efba8b24058da9c70cc77c35a Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Thu, 10 Sep 2009 19:32:54 +0200 Subject: [PATCH] Add support for iPhones and recent iPods. 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 | 1 + configure.in | 9 ++ src/Makefile.am | 10 +- src/bsd/device.c | 74 ++++++++- src/bsd/tunemu.c | 386 +++++++++++++++++++++++++++++++++++++++++++++++ src/bsd/tunemu.h | 32 ++++ 6 files changed, 503 insertions(+), 9 deletions(-) create mode 100644 src/bsd/tunemu.c create mode 100644 src/bsd/tunemu.h diff --git a/THANKS b/THANKS index 2daa8f16..ed57da59 100644 --- 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 diff --git a/configure.in b/configure.in index 964dd27a..eff93a08 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index 10f07af5..501fdf66 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/bsd/device.c b/src/bsd/device.c index 2e8908af..fe85d10f 100644 --- a/src/bsd/device.c +++ b/src/bsd/device.c @@ -29,12 +29,19 @@ #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 index 00000000..f532b04f --- /dev/null +++ b/src/bsd/tunemu.c @@ -0,0 +1,386 @@ +/* + * tunemu - Tun device emulation for Darwin + * Copyright (C) 2009 Friedrich Schöller + * + * 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 . + * + */ + +#include "tunemu.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000..42b1785b --- /dev/null +++ b/src/bsd/tunemu.h @@ -0,0 +1,32 @@ +/* + * tunemu - Tun device emulation for Darwin + * Copyright (C) 2009 Friedrich Schöller + * + * 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 . + * + */ + +#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 -- 2.39.5