Reformat all code using astyle.
[tinc] / src / bsd / tunemu.c
1 /*
2  *  tunemu - Tun device emulation for Darwin
3  *  Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
4  *
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.
9  *
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.
14  *
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/>.
17  *
18  */
19
20 #include "tunemu.h"
21
22 #include <sys/socket.h>
23 #include <unistd.h>
24 #include <sys/ioctl.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <memory.h>
28 #include <util.h>
29 #include <pcap.h>
30 #include <stdarg.h>
31 #include <errno.h>
32 #include <stdint.h>
33 #include <stdint.h>
34 #include <ctype.h>
35 #include <fcntl.h>
36
37 #define PPPPROTO_CTL 1
38
39 #define PPP_IP          0x21
40 #define PPP_IPV6        0x57
41
42 #define SC_LOOP_TRAFFIC 0x00000200
43
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)
51
52 struct sockaddr_ppp {
53         u_int8_t ppp_len;
54         u_int8_t ppp_family;
55         u_int16_t ppp_proto;
56         u_int32_t ppp_cookie;
57 };
58
59 enum NPmode {
60         NPMODE_PASS,
61         NPMODE_DROP,
62         NPMODE_ERROR,
63         NPMODE_QUEUE
64 };
65
66 struct npioctl {
67         int protocol;
68         enum NPmode mode;
69 };
70
71 #define PPP_KEXT_PATH "/System/Library/Extensions/PPP.kext"
72
73 #define ERROR_BUFFER_SIZE 1024
74
75 char tunemu_error[ERROR_BUFFER_SIZE];
76
77 static int pcap_use_count = 0;
78 static pcap_t *pcap = NULL;
79
80 static int data_buffer_length = 0;
81 static char *data_buffer = NULL;
82
83 static void tun_error(char *format, ...) {
84         va_list vl;
85         va_start(vl, format);
86         vsnprintf(tunemu_error, ERROR_BUFFER_SIZE, format, vl);
87         va_end(vl);
88 }
89
90 static void tun_noerror() {
91         *tunemu_error = 0;
92 }
93
94 static void closeall() {
95         int fd = getdtablesize();
96
97         while(fd--) {
98                 close(fd);
99         }
100
101         open("/dev/null", O_RDWR, 0);
102         dup(0);
103         dup(0);
104 }
105
106 static int ppp_load_kext() {
107         int pid = fork();
108
109         if(pid < 0) {
110                 tun_error("fork for ppp kext: %s", strerror(errno));
111                 return -1;
112         }
113
114         if(pid == 0) {
115                 closeall();
116                 execle("/sbin/kextload", "kextload", PPP_KEXT_PATH, NULL, NULL);
117                 exit(1);
118         }
119
120         int status;
121
122         while(waitpid(pid, &status, 0) < 0) {
123                 if(errno == EINTR) {
124                         continue;
125                 }
126
127                 tun_error("waitpid for ppp kext: %s", strerror(errno));
128                 return -1;
129         }
130
131         if(WEXITSTATUS(status) != 0) {
132                 tun_error("could not load ppp kext \"%s\"", PPP_KEXT_PATH);
133                 return -1;
134         }
135
136         tun_noerror();
137         return 0;
138 }
139
140 static int ppp_new_instance() {
141         // create ppp socket
142         int ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
143
144         if(ppp_sockfd < 0) {
145                 if(ppp_load_kext() < 0) {
146                         return -1;
147                 }
148
149                 ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
150
151                 if(ppp_sockfd < 0) {
152                         tun_error("creating ppp socket: %s", strerror(errno));
153                         return -1;
154                 }
155         }
156
157         // connect to ppp procotol
158         struct sockaddr_ppp pppaddr;
159         pppaddr.ppp_len = sizeof(struct sockaddr_ppp);
160         pppaddr.ppp_family = AF_PPP;
161         pppaddr.ppp_proto = PPPPROTO_CTL;
162         pppaddr.ppp_cookie = 0;
163
164         if(connect(ppp_sockfd, (struct sockaddr *)&pppaddr, sizeof(struct sockaddr_ppp)) < 0) {
165                 tun_error("connecting ppp socket: %s", strerror(errno));
166                 close(ppp_sockfd);
167                 return -1;
168         }
169
170         tun_noerror();
171         return ppp_sockfd;
172 }
173
174 static int ppp_new_unit(int *unit_number) {
175         int fd = ppp_new_instance();
176
177         if(fd < 0) {
178                 return -1;
179         }
180
181         // create ppp unit
182         if(ioctl(fd, PPPIOCNEWUNIT, unit_number) < 0) {
183                 tun_error("creating ppp unit: %s", strerror(errno));
184                 close(fd);
185                 return -1;
186         }
187
188         tun_noerror();
189         return fd;
190 }
191
192 static int ppp_setup_unit(int unit_fd) {
193         // send traffic to program
194         int flags = SC_LOOP_TRAFFIC;
195
196         if(ioctl(unit_fd, PPPIOCSFLAGS, &flags) < 0) {
197                 tun_error("setting ppp loopback mode: %s", strerror(errno));
198                 return -1;
199         }
200
201         // allow packets
202         struct npioctl npi;
203         npi.protocol = PPP_IP;
204         npi.mode = NPMODE_PASS;
205
206         if(ioctl(unit_fd, PPPIOCSNPMODE, &npi) < 0) {
207                 tun_error("starting ppp unit: %s", strerror(errno));
208                 return -1;
209         }
210
211         tun_noerror();
212         return 0;
213 }
214
215 static int open_pcap() {
216         if(pcap != NULL) {
217                 pcap_use_count++;
218                 return 0;
219         }
220
221         char errbuf[PCAP_ERRBUF_SIZE];
222         pcap = pcap_open_live("lo0", BUFSIZ, 0, 1, errbuf);
223         pcap_use_count = 1;
224
225         if(pcap == NULL) {
226                 tun_error("opening pcap: %s", errbuf);
227                 return -1;
228         }
229
230         tun_noerror();
231         return 0;
232 }
233
234 static void close_pcap() {
235         if(pcap == NULL) {
236                 return;
237         }
238
239         pcap_use_count--;
240
241         if(pcap_use_count == 0) {
242                 pcap_close(pcap);
243                 pcap = NULL;
244         }
245 }
246
247 static void allocate_data_buffer(int size) {
248         if(data_buffer_length < size) {
249                 free(data_buffer);
250                 data_buffer_length = size;
251                 data_buffer = malloc(data_buffer_length);
252         }
253 }
254
255 static void make_device_name(tunemu_device device, int unit_number) {
256         snprintf(device, sizeof(tunemu_device), "ppp%d", unit_number);
257 }
258
259 static int check_device_name(tunemu_device device) {
260         if(strlen(device) < 4) {
261                 return -1;
262         }
263
264         int unit_number = atoi(device + 3);
265
266         if(unit_number < 0 || unit_number > 999) {
267                 return -1;
268         }
269
270         tunemu_device compare;
271         make_device_name(compare, unit_number);
272
273         if(strcmp(device, compare) != 0) {
274                 return -1;
275         }
276
277         return 0;
278 }
279
280 int tunemu_open(tunemu_device device) {
281         int ppp_unit_number = -1;
282
283         if(device[0] != 0) {
284                 if(check_device_name(device) < 0) {
285                         tun_error("invalid device name \"%s\"", device);
286                         return -1;
287                 }
288
289                 ppp_unit_number = atoi(device + 3);
290         }
291
292         int ppp_unit_fd = ppp_new_unit(&ppp_unit_number);
293
294         if(ppp_unit_fd < 0) {
295                 return -1;
296         }
297
298         if(ppp_setup_unit(ppp_unit_fd) < 0) {
299                 close(ppp_unit_fd);
300                 return -1;
301         }
302
303         if(open_pcap() < 0) {
304                 close(ppp_unit_fd);
305                 return -1;
306         }
307
308         make_device_name(device, ppp_unit_number);
309
310         return ppp_unit_fd;
311 }
312
313 int tunemu_close(int ppp_sockfd) {
314         int ret = close(ppp_sockfd);
315
316         if(ret == 0) {
317                 close_pcap();
318         }
319
320         return ret;
321 }
322
323 int tunemu_read(int ppp_sockfd, char *buffer, int length) {
324         allocate_data_buffer(length + 2);
325
326         length = read(ppp_sockfd, data_buffer, length + 2);
327
328         if(length < 0) {
329                 tun_error("reading packet: %s", strerror(errno));
330                 return length;
331         }
332
333         tun_noerror();
334
335         length -= 2;
336
337         if(length < 0) {
338                 return 0;
339         }
340
341         memcpy(buffer, data_buffer + 2, length);
342
343         return length;
344 }
345
346 int tunemu_write(int ppp_sockfd, char *buffer, int length) {
347         allocate_data_buffer(length + 4);
348
349         data_buffer[0] = 0x02;
350         data_buffer[1] = 0x00;
351         data_buffer[2] = 0x00;
352         data_buffer[3] = 0x00;
353
354         memcpy(data_buffer + 4, buffer, length);
355
356         if(pcap == NULL) {
357                 tun_error("pcap not open");
358                 return -1;
359         }
360
361         length = pcap_inject(pcap, data_buffer, length + 4);
362
363         if(length < 0) {
364                 tun_error("injecting packet: %s", pcap_geterr(pcap));
365                 return length;
366         }
367
368         tun_noerror();
369
370         length -= 4;
371
372         if(length < 0) {
373                 return 0;
374         }
375
376         return length;
377 }