diff --git a/Makefile b/Makefile
index 36adf9f..2e4b5a6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
-LDFLAGS = `sh osflags ld`
-CFLAGS = -c -g `sh osflags c`
-TUN_DEV_FILE = `sh osflags dev`
+LDFLAGS = `sh osflags ld $(MODE)`
+CFLAGS = -c -g `sh osflags c $(MODE)`
+TUN_DEV_FILE = `sh osflags dev $(MODE)`
 
 all: hans
 
@@ -44,5 +44,8 @@ time.o: time.cpp time.h
 	g++ -c time.cpp $(CFLAGS)
 
 clean:
-	rm -f tun.o sha1.o main.o client.o server.o auth.o worker.o time.o tun_dev.o echo.o exception.o utility.o hans
+	rm -f tun.o sha1.o main.o client.o server.o auth.o worker.o time.o tun_dev.o echo.o exception.o utility.o tunemu.o hans
 
+
+tunemu.o: tunemu.h tunemu.c
+	gcc -c tunemu.c -o tunemu.o
diff --git a/main.cpp b/main.cpp
index 8f60ffd..171318e 100644
--- a/main.cpp
+++ b/main.cpp
@@ -34,7 +34,7 @@
 void usage()
 {
 	printf(
-		"Hans - IP over ICMP version 0.2\n\n"
+		"Hans - IP over ICMP version 0.3\n\n"
 		"RUN AS SERVER\n"
 		"  hans -s network [-fr] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu]\n\n"
 		"RUN AS CLIENT\n"
diff --git a/osflags b/osflags
index b8f91ea..5c5155b 100644
--- a/osflags
+++ b/osflags
@@ -1,9 +1,17 @@
 #!/bin/sh
 
 OS=`uname | tr "a-z" "A-Z"`
+MODE=`echo "$2" | tr "a-z" "A-Z"`
 
 case $1 in
 ld)
+	case $OS in
+		DARWIN)
+			if [ "$MODE" == TUNEMU ]; then
+				echo tunemu.o -lpcap
+			fi
+		;;
+	esac
 	;;
 c)
 	FLAGS=-D$OS
@@ -28,6 +36,13 @@ dev)
 		OPENBSD)
 			echo tun_dev_openbsd.c
 		;;
+		DARWIN)
+			if [ "$MODE" == TUNEMU ]; then
+				echo tun_dev_darwin_emu.c
+			else
+				echo tun_dev_generic.c
+			fi
+		;;
 		*)
 			echo tun_dev_generic.c
 		;;
diff --git a/tun_dev_darwin_emu.c b/tun_dev_darwin_emu.c
new file mode 100644
index 0000000..5132f3b
--- /dev/null
+++ b/tun_dev_darwin_emu.c
@@ -0,0 +1,44 @@
+/*  
+ VTun - Virtual Tunnel over TCP/IP network.
+ 
+ Copyright (C) 1998-2000  Maxim Krasnyansky <max_mk@yahoo.com>
+ 
+ VTun has been derived from VPPP package by Maxim Krasnyansky. 
+ 
+ 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 2 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.
+ */
+
+#include "tunemu.h"
+
+#include <syslog.h>
+
+int tun_open(char *dev)
+{
+	int fd = tun_emu_open(dev);
+	if (fd < 0)
+		syslog(LOG_ERR, tun_emu_error);
+	return fd;
+}
+
+int tun_close(int fd, char *dev)
+{
+	return tun_emu_close(fd);
+}
+
+int tun_write(int fd, char *buf, int len)
+{
+	return tun_emu_write(fd, buf, len);
+}
+
+int tun_read(int fd, char *buf, int len)
+{
+	return tun_emu_read(fd, buf, len);
+}
diff --git a/tunemu.c b/tunemu.c
new file mode 100644
index 0000000..87476b0
--- /dev/null
+++ b/tunemu.c
@@ -0,0 +1,283 @@
+/*
+ *  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>
+
+#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 ERROR_BUFFER_SIZE 1024
+
+char tun_emu_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(tun_emu_error, ERROR_BUFFER_SIZE, format, vl);
+	va_end(vl);
+}
+
+static void tun_noerror()
+{
+	*tun_emu_error = 0;
+}
+
+static int ppp_new_instance()
+{
+	// create ppp socket
+    int 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)
+	{
+		data_buffer_length = size;
+		free(data_buffer);
+		data_buffer = malloc(data_buffer_length);
+	}
+}
+
+int tun_emu_open(tun_emu_device device)
+{
+	int ppp_unit_number = -1;
+	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;
+	}
+
+	snprintf(device, 7, "ppp%d", ppp_unit_number);
+
+	return ppp_unit_fd;
+}
+
+int tun_emu_close(int ppp_sockfd)
+{
+	int ret = close(ppp_sockfd);
+
+	if (ret == 0)
+		close_pcap();
+
+	return ret;
+}
+
+int tun_emu_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 tun_emu_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);
+
+	length = pcap_inject(pcap, data_buffer, length + 4);
+	if (length < 0)
+		tun_error("injecting packet: %s", pcap_geterr(pcap));
+
+	length -= 4;
+	if (length < 0)
+		return 0;
+
+	return length;
+}
diff --git a/tunemu.h b/tunemu.h
new file mode 100644
index 0000000..6237591
--- /dev/null
+++ b/tunemu.h
@@ -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 TUN_EMU_H
+#define TUN_EMU_H
+
+typedef char tun_emu_device[7];
+
+extern char tun_emu_error[];
+
+int tun_emu_open(tun_emu_device dev);
+int tun_emu_close(int fd);
+int tun_emu_read(int fd, char *buffer, int length);
+int tun_emu_write(int fd, char *buffer, int length);
+
+#endif