2 * tunemu - Tun device emulation for Darwin
3 * Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/socket.h>
24 #include <sys/ioctl.h>
37 #define PPPPROTO_CTL 1
42 #define SC_LOOP_TRAFFIC 0x00000200
44 #define PPPIOCNEWUNIT _IOWR('t', 62, int)
45 #define PPPIOCSFLAGS _IOW('t', 89, int)
46 #define PPPIOCSNPMODE _IOW('t', 75, struct npioctl)
47 #define PPPIOCATTCHAN _IOW('t', 56, int)
48 #define PPPIOCGCHAN _IOR('t', 55, int)
49 #define PPPIOCCONNECT _IOW('t', 58, int)
50 #define PPPIOCGUNIT _IOR('t', 86, int)
71 #define PPP_KEXT_PATH "/System/Library/Extensions/PPP.kext"
73 #define ERROR_BUFFER_SIZE 1024
75 char tunemu_error[ERROR_BUFFER_SIZE];
77 static int pcap_use_count = 0;
78 static pcap_t *pcap = NULL;
80 static size_t data_buffer_length = 0;
81 static uint8_t *data_buffer = NULL;
83 static void tun_error(char *format, ...) ATTR_FORMAT(printf, 1, 2);
84 static void tun_error(char *format, ...) {
87 vsnprintf(tunemu_error, sizeof(tunemu_error), format, vl);
91 static void tun_noerror() {
95 static void closeall() {
96 int fd = getdtablesize();
102 open("/dev/null", O_RDWR, 0);
107 static int ppp_load_kext() {
111 tun_error("fork for ppp kext: %s", strerror(errno));
117 execle("/sbin/kextload", "kextload", PPP_KEXT_PATH, NULL, NULL);
123 while(waitpid(pid, &status, 0) < 0) {
128 tun_error("waitpid for ppp kext: %s", strerror(errno));
132 if(WEXITSTATUS(status) != 0) {
133 tun_error("could not load ppp kext \"%s\"", PPP_KEXT_PATH);
141 static int ppp_new_instance() {
143 int ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
146 if(ppp_load_kext() < 0) {
150 ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
153 tun_error("creating ppp socket: %s", strerror(errno));
158 // connect to ppp procotol
159 struct sockaddr_ppp pppaddr;
160 pppaddr.ppp_len = sizeof(struct sockaddr_ppp);
161 pppaddr.ppp_family = AF_PPP;
162 pppaddr.ppp_proto = PPPPROTO_CTL;
163 pppaddr.ppp_cookie = 0;
165 if(connect(ppp_sockfd, (struct sockaddr *)&pppaddr, sizeof(struct sockaddr_ppp)) < 0) {
166 tun_error("connecting ppp socket: %s", strerror(errno));
175 static int ppp_new_unit(int *unit_number) {
176 int fd = ppp_new_instance();
183 if(ioctl(fd, PPPIOCNEWUNIT, unit_number) < 0) {
184 tun_error("creating ppp unit: %s", strerror(errno));
193 static int ppp_setup_unit(int unit_fd) {
194 // send traffic to program
195 int flags = SC_LOOP_TRAFFIC;
197 if(ioctl(unit_fd, PPPIOCSFLAGS, &flags) < 0) {
198 tun_error("setting ppp loopback mode: %s", strerror(errno));
204 npi.protocol = PPP_IP;
205 npi.mode = NPMODE_PASS;
207 if(ioctl(unit_fd, PPPIOCSNPMODE, &npi) < 0) {
208 tun_error("starting ppp unit: %s", strerror(errno));
216 static int open_pcap() {
222 char errbuf[PCAP_ERRBUF_SIZE];
223 pcap = pcap_open_live("lo0", BUFSIZ, 0, 1, errbuf);
227 tun_error("opening pcap: %s", errbuf);
235 static void close_pcap() {
242 if(pcap_use_count == 0) {
248 static void allocate_data_buffer(size_t size) {
249 if(data_buffer_length < size) {
251 data_buffer_length = size;
252 data_buffer = malloc(data_buffer_length);
256 static void make_device_name(tunemu_device device, int unit_number) {
257 snprintf(device, sizeof(tunemu_device), "ppp%d", unit_number);
260 static int check_device_name(tunemu_device device) {
261 if(strlen(device) < 4) {
265 int unit_number = atoi(device + 3);
267 if(unit_number < 0 || unit_number > 999) {
271 tunemu_device compare;
272 make_device_name(compare, unit_number);
274 if(strcmp(device, compare) != 0) {
281 int tunemu_open(tunemu_device device) {
282 int ppp_unit_number = -1;
285 if(check_device_name(device) < 0) {
286 tun_error("invalid device name \"%s\"", device);
290 ppp_unit_number = atoi(device + 3);
293 int ppp_unit_fd = ppp_new_unit(&ppp_unit_number);
295 if(ppp_unit_fd < 0) {
299 if(ppp_setup_unit(ppp_unit_fd) < 0) {
304 if(open_pcap() < 0) {
309 make_device_name(device, ppp_unit_number);
314 int tunemu_close(int ppp_sockfd) {
315 int ret = close(ppp_sockfd);
324 ssize_t tunemu_read(int ppp_sockfd, uint8_t *buffer, size_t buflen) {
325 allocate_data_buffer(buflen + 2);
327 ssize_t length = read(ppp_sockfd, data_buffer, buflen + 2);
330 tun_error("reading packet: %s", strerror(errno));
342 memcpy(buffer, data_buffer + 2, length);
347 ssize_t tunemu_write(uint8_t *buffer, size_t buflen) {
348 allocate_data_buffer(buflen + 4);
350 data_buffer[0] = 0x02;
351 data_buffer[1] = 0x00;
352 data_buffer[2] = 0x00;
353 data_buffer[3] = 0x00;
355 memcpy(data_buffer + 4, buffer, buflen);
358 tun_error("pcap not open");
362 ssize_t length = pcap_inject(pcap, data_buffer, buflen + 4);
365 tun_error("injecting packet: %s", pcap_geterr(pcap));