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.
This commit is contained in:
Guus Sliepen 2009-09-10 19:32:54 +02:00
parent ff946d0423
commit 4a5d42178c
6 changed files with 503 additions and 9 deletions

1
THANKS
View file

@ -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

View 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

View 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

View file

@ -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;
}

386
src/bsd/tunemu.c Normal file
View file

@ -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;
}

32
src/bsd/tunemu.h Normal file
View file

@ -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