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