1 /*
2  *  tunemu - Tun device emulation for Darwin
3  *
4  *  Copyright (c) 2009-2013 Friedrich Schöller <hans@schoeller.se>
5  *  All rights reserved.
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions are met:
9  *  1. Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  *  2. Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  *
15  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  *  POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
29 #include "tunemu.h"
30 
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <stdint.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/ioctl.h>
38 #include <memory.h>
39 #include <util.h>
40 #include <pcap.h>
41 #include <stdarg.h>
42 #include <errno.h>
43 #include <ctype.h>
44 #include <fcntl.h>
45 #include <string.h>
46 
47 #define PPPPROTO_CTL 1
48 
49 #define PPP_IP   0x21
50 #define PPP_IPV6 0x57
51 
52 #define SC_LOOP_TRAFFIC 0x00000200
53 
54 #define PPPIOCNEWUNIT   _IOWR('t', 62, int)
55 #define PPPIOCSFLAGS    _IOW('t', 89, int)
56 #define PPPIOCSNPMODE   _IOW('t', 75, struct npioctl)
57 #define PPPIOCATTCHAN   _IOW('t', 56, int)
58 #define PPPIOCGCHAN     _IOR('t', 55, int)
59 #define PPPIOCCONNECT   _IOW('t', 58, int)
60 #define PPPIOCGUNIT     _IOR('t', 86, int)
61 
62 struct sockaddr_ppp
63 {
64     u_int8_t ppp_len;
65     u_int8_t ppp_family;
66     u_int16_t ppp_proto;
67     u_int32_t ppp_cookie;
68 };
69 
70 enum NPmode
71 {
72     NPMODE_PASS,
73     NPMODE_DROP,
74     NPMODE_ERROR,
75     NPMODE_QUEUE
76 };
77 
78 struct npioctl
79 {
80     int protocol;
81     enum NPmode mode;
82 };
83 
84 #define PPP_KEXT_PATH "/System/Library/Extensions/PPP.kext"
85 
86 #define ERROR_BUFFER_SIZE 1024
87 
88 char tunemu_error[ERROR_BUFFER_SIZE];
89 
90 static int pcap_use_count = 0;
91 static pcap_t *pcap = NULL;
92 
93 static int data_buffer_length = 0;
94 static char *data_buffer = NULL;
95 
tun_error(char * format,...)96 static void tun_error(char *format, ...)
97 {
98     va_list vl;
99     va_start(vl, format);
100     vsnprintf(tunemu_error, ERROR_BUFFER_SIZE, format, vl);
101     va_end(vl);
102 }
103 
tun_noerror(void)104 static void tun_noerror(void)
105 {
106     *tunemu_error = 0;
107 }
108 
closeall(void)109 static void closeall(void)
110 {
111     int fd = getdtablesize();
112     while (fd--)
113         close(fd);
114 
115     open("/dev/null", O_RDWR, 0);
116     dup(0);
117     dup(0);
118 }
119 
ppp_load_kext(void)120 static int ppp_load_kext(void)
121 {
122     int pid = fork();
123     if (pid < 0)
124     {
125         tun_error("fork for ppp kext: %s", strerror(errno));
126         return -1;
127     }
128 
129     if (pid == 0)
130     {
131         closeall();
132         execle("/sbin/kextload", "kextload", PPP_KEXT_PATH, NULL, NULL);
133         exit(1);
134     }
135 
136     int status;
137     while (waitpid(pid, &status, 0) < 0)
138     {
139         if (errno == EINTR)
140             continue;
141 
142         tun_error("waitpid for ppp kext: %s", strerror(errno));
143         return -1;
144     }
145 
146     if (WEXITSTATUS(status) != 0)
147     {
148         tun_error("could not load ppp kext \"%s\"", PPP_KEXT_PATH);
149         return -1;
150     }
151 
152     tun_noerror();
153     return 0;
154 }
155 
ppp_new_instance(void)156 static int ppp_new_instance(void)
157 {
158     // create ppp socket
159     int ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
160     if (ppp_sockfd < 0)
161     {
162         if (ppp_load_kext() < 0)
163             return -1;
164 
165         ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
166         if (ppp_sockfd < 0)
167         {
168             tun_error("creating ppp socket: %s", strerror(errno));
169             return -1;
170         }
171     }
172 
173     // connect to ppp procotol
174     struct sockaddr_ppp pppaddr;
175     pppaddr.ppp_len = sizeof(struct sockaddr_ppp);
176     pppaddr.ppp_family = AF_PPP;
177     pppaddr.ppp_proto = PPPPROTO_CTL;
178     pppaddr.ppp_cookie = 0;
179     if (connect(ppp_sockfd, (struct sockaddr *)&pppaddr, sizeof(struct sockaddr_ppp)) < 0)
180     {
181         tun_error("connecting ppp socket: %s", strerror(errno));
182         close(ppp_sockfd);
183         return -1;
184     }
185 
186     tun_noerror();
187     return ppp_sockfd;
188 }
189 
ppp_new_unit(int * unit_number)190 static int ppp_new_unit(int *unit_number)
191 {
192     int fd = ppp_new_instance();
193     if (fd < 0)
194         return -1;
195 
196     // create ppp unit
197     if (ioctl(fd, PPPIOCNEWUNIT, unit_number) < 0)
198     {
199         tun_error("creating ppp unit: %s", strerror(errno));
200         close(fd);
201         return -1;
202     }
203 
204     tun_noerror();
205     return fd;
206 }
207 
ppp_setup_unit(int unit_fd)208 static int ppp_setup_unit(int unit_fd)
209 {
210     // send traffic to program
211     int flags = SC_LOOP_TRAFFIC;
212     if (ioctl(unit_fd, PPPIOCSFLAGS, &flags) < 0)
213     {
214         tun_error("setting ppp loopback mode: %s", strerror(errno));
215         return -1;
216     }
217 
218     // allow packets
219     struct npioctl npi;
220     npi.protocol = PPP_IP;
221     npi.mode = NPMODE_PASS;
222     if (ioctl(unit_fd, PPPIOCSNPMODE, &npi) < 0)
223     {
224         tun_error("starting ppp unit: %s", strerror(errno));
225         return -1;
226     }
227 
228     tun_noerror();
229     return 0;
230 }
231 
open_pcap(void)232 static int open_pcap(void)
233 {
234     if (pcap != NULL)
235     {
236         pcap_use_count++;
237         return 0;
238     }
239 
240     char errbuf[PCAP_ERRBUF_SIZE];
241     pcap = pcap_open_live("lo0", BUFSIZ, 0, 1, errbuf);
242     pcap_use_count = 1;
243 
244     if (pcap == NULL)
245     {
246         tun_error("opening pcap: %s", errbuf);
247         return -1;
248     }
249 
250     tun_noerror();
251     return 0;
252 }
253 
close_pcap(void)254 static void close_pcap(void)
255 {
256     if (pcap == NULL)
257         return;
258 
259     pcap_use_count--;
260     if (pcap_use_count == 0)
261     {
262         pcap_close(pcap);
263         pcap = NULL;
264     }
265 }
266 
allocate_data_buffer(int size)267 static void allocate_data_buffer(int size)
268 {
269     if (data_buffer_length < size)
270     {
271         free(data_buffer);
272         data_buffer_length = size;
273         data_buffer = malloc(data_buffer_length);
274     }
275 }
276 
make_device_name(tunemu_device device,int unit_number)277 static void make_device_name(tunemu_device device, int unit_number)
278 {
279     snprintf(device, sizeof(tunemu_device), "ppp%d", unit_number);
280 }
281 
check_device_name(tunemu_device device)282 static int check_device_name(tunemu_device device)
283 {
284     if (strlen(device) < 4)
285         return -1;
286 
287     int unit_number = atoi(device + 3);
288     if (unit_number < 0 || unit_number > 999)
289         return -1;
290 
291     tunemu_device compare;
292     make_device_name(compare, unit_number);
293 
294     if (strcmp(device, compare) != 0)
295         return -1;
296 
297     return 0;
298 }
299 
tunemu_open(tunemu_device device)300 int tunemu_open(tunemu_device device)
301 {
302     int ppp_unit_number = -1;
303     if (device[0] != 0)
304     {
305         if (check_device_name(device) < 0)
306         {
307             tun_error("invalid device name \"%s\"", device);
308             return -1;
309         }
310 
311         ppp_unit_number = atoi(device + 3);
312     }
313 
314     int ppp_unit_fd = ppp_new_unit(&ppp_unit_number);
315     if (ppp_unit_fd < 0)
316         return -1;
317 
318     if (ppp_setup_unit(ppp_unit_fd) < 0)
319     {
320         close(ppp_unit_fd);
321         return -1;
322     }
323 
324     if (open_pcap() < 0)
325     {
326         close(ppp_unit_fd);
327         return -1;
328     }
329 
330     make_device_name(device, ppp_unit_number);
331 
332     return ppp_unit_fd;
333 }
334 
tunemu_close(int ppp_sockfd)335 int tunemu_close(int ppp_sockfd)
336 {
337     int ret = close(ppp_sockfd);
338 
339     if (ret == 0)
340         close_pcap();
341 
342     return ret;
343 }
344 
tunemu_read(int ppp_sockfd,char * buffer,int length)345 int tunemu_read(int ppp_sockfd, char *buffer, int length)
346 {
347     allocate_data_buffer(length + 2);
348 
349     length = read(ppp_sockfd, data_buffer, length + 2);
350     if (length < 0)
351     {
352         tun_error("reading packet: %s", strerror(errno));
353         return length;
354     }
355     tun_noerror();
356 
357     length -= 2;
358     if (length < 0)
359         return 0;
360 
361     memcpy(buffer, data_buffer + 2, length);
362 
363     return length;
364 }
365 
tunemu_write(int ppp_sockfd,char * buffer,int length)366 int tunemu_write(int ppp_sockfd, char *buffer, int length)
367 {
368     allocate_data_buffer(length + 4);
369 
370     data_buffer[0] = 0x02;
371     data_buffer[1] = 0x00;
372     data_buffer[2] = 0x00;
373     data_buffer[3] = 0x00;
374 
375     memcpy(data_buffer + 4, buffer, length);
376 
377     if (pcap == NULL)
378     {
379         tun_error("pcap not open");
380         return -1;
381     }
382 
383     length = pcap_inject(pcap, data_buffer, length + 4);
384     if (length < 0)
385     {
386         tun_error("injecting packet: %s", pcap_geterr(pcap));
387         return length;
388     }
389     tun_noerror();
390 
391     length -= 4;
392     if (length < 0)
393         return 0;
394 
395     return length;
396 }
397