f19366873c75b661a3ff2e8c84d487c71186ebd3
[debian/uanytun.git] / src / bsd / tun.c
1 /*
2  *  uAnytun
3  *
4  *  uAnytun is a tiny implementation of SATP. Unlike Anytun which is a full
5  *  featured implementation uAnytun has no support for multiple connections
6  *  or synchronisation. It is a small single threaded implementation intended
7  *  to act as a client on small platforms.
8  *  The secure anycast tunneling protocol (satp) defines a protocol used
9  *  for communication between any combination of unicast and anycast
10  *  tunnel endpoints.  It has less protocol overhead than IPSec in Tunnel
11  *  mode and allows tunneling of every ETHER TYPE protocol (e.g.
12  *  ethernet, ip, arp ...). satp directly includes cryptography and
13  *  message authentication based on the methodes used by SRTP.  It is
14  *  intended to deliver a generic, scaleable and secure solution for
15  *  tunneling and relaying of packets of any protocol.
16  *  
17  *
18  *  Copyright (C) 2007-2008 Christian Pointner <equinox@anytun.org>
19  *
20  *  This file is part of uAnytun.
21  *
22  *  uAnytun is free software: you can redistribute it and/or modify
23  *  it under the terms of the GNU General Public License version 3 as
24  *  published by the Free Software Foundation.
25  *
26  *  uAnytun is distributed in the hope that it will be useful,
27  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
28  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29  *  GNU General Public License for more details.
30  *
31  *  You should have received a copy of the GNU General Public License
32  *  along with uAnytun. If not, see <http://www.gnu.org/licenses/>.
33  */
34
35 #include "datatypes.h"
36
37 #include "tun.h"
38
39 #include "tun_helper.h"
40
41 #include "log.h"
42
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <errno.h>
46 #include <sys/wait.h>
47 #include <sys/socket.h>
48 #include <net/if.h>
49 #include <net/if_tun.h>
50 #include <sys/ioctl.h>
51 #include <sys/types.h>
52 #include <sys/uio.h>
53 #include <netinet/in_systm.h>
54 #include <netinet/in.h>
55 #include <netinet/ip.h>
56 #define DEVICE_FILE_MAX 255
57
58 int tun_init(tun_device_t* dev, const char* dev_name, const char* dev_type, const char* ifcfg_addr, u_int16_t ifcfg_prefix)
59 {
60   if(!dev) 
61     return -1;
62  
63   tun_conf(dev, dev_name, dev_type, ifcfg_addr, ifcfg_prefix, 1400);
64   dev->actual_name_ = NULL;
65
66   char* device_file = NULL;
67   char* actual_name_start = NULL;
68   int dynamic = 1;
69   if(dev_name) {
70     asprintf(&device_file, "/dev/%s", dev_name);
71     dynamic = 0;
72   }
73 #if defined(__GNUC__) && defined(__OpenBSD__)
74   else if(dev->type_ == TYPE_TUN || dev->type_ == TYPE_TAP) {
75     asprintf(&device_file, "/dev/tun");
76     actual_name_start = "tun";
77   }
78 #else
79   else if(dev->type_ == TYPE_TUN) {
80     asprintf(&device_file, "/dev/tun");
81     actual_name_start = "tun";
82   }
83   else if(dev->type_ == TYPE_TAP) {
84     asprintf(&device_file, "/dev/tap");
85     actual_name_start = "tap";
86   }
87 #endif
88   else {
89     log_printf(ERROR, "unable to recognize type of device (tun or tap)");
90     tun_close(dev);
91     return -1;
92   }
93   if(!device_file) {
94     log_printf(ERROR, "can't open device file: memory error");
95     tun_close(dev);
96     return -2;
97   }
98
99   u_int32_t dev_id=0;
100   if(dynamic) {
101     for(; dev_id <= DEVICE_FILE_MAX; ++dev_id) {
102       char* device_file_tmp = NULL;
103       asprintf(&device_file_tmp, "%s%d", device_file, dev_id);
104
105       if(!device_file_tmp) {
106         log_printf(ERROR, "can't open device file: memory error");
107         free(device_file);
108         tun_close(dev);
109         return -2;
110       }
111         
112       dev->fd_ = open(device_file_tmp, O_RDWR);
113       free(device_file_tmp);
114       if(dev->fd_ >= 0)
115         break;
116     }
117   }
118   else
119     dev->fd_ = open(device_file, O_RDWR);
120   free(device_file);
121
122   if(dev->fd_ < 0) {
123     if(dynamic)
124       log_printf(ERROR, "can't open device file dynamically: no unused node left");
125     else
126       log_printf(ERROR, "can't open device file (%s): %s", device_file, strerror(errno));
127     
128     tun_close(dev);
129     return -1;
130   }
131
132   if(dynamic)
133     asprintf(&(dev->actual_name_), "%s%d", actual_name_start, dev_id);
134   else
135     dev->actual_name_ = strdup(dev_name);
136
137   if(!dev->actual_name_) {
138     log_printf(ERROR, "can't open device file: memory error");
139     tun_close(dev);
140     return -2;
141   }
142
143   int ret = tun_init_post(dev);
144   if(ret) {
145     tun_close(dev);
146     return ret;
147   }
148
149   if(ifcfg_addr)
150     tun_do_ifconfig(dev);
151
152   return 0;
153 }
154
155
156 #if defined(__GNUC__) && defined(__OpenBSD__)
157
158 int tun_init_post(tun_device_t* dev)
159 {
160   if(!dev)
161     return -1;
162
163   dev->with_pi_ = 1;
164   if(dev->type_ == TYPE_TAP)
165     dev->with_pi_ = 0;
166   
167   struct tuninfo ti;  
168
169   if(ioctl(dev->fd_, TUNGIFINFO, &ti) < 0) {
170     log_printf(ERROR, "can't enable multicast for interface: %s", strerror(errno));
171     return -1;
172   }  
173
174   ti.flags |= IFF_MULTICAST;
175   if(dev->type_ == TYPE_TUN)
176     ti.flags &= ~IFF_POINTOPOINT;
177   
178   if(ioctl(dev->fd_, TUNSIFINFO, &ti) < 0) {
179     log_printf(ERROR, "can't enable multicast for interface: %s", strerror(errno));
180     return -1;
181   }
182   return 0;
183 }
184
185 #elif defined(__GNUC__) && defined(__FreeBSD__)
186
187 int tun_init_post(tun_device_t* dev)
188 {
189   if(!dev)
190     return -1;
191
192   dev->with_pi_ = 1;
193   if(dev->type_ == TYPE_TAP)
194     dev->with_pi_ = 0;
195
196   if(dev->type_ == TYPE_TUN) {
197     int arg = 0;
198     if(ioctl(dev->fd_, TUNSLMODE, &arg) < 0) {
199       log_printf(ERROR, "can't disable link-layer mode for interface: %s", strerror(errno));
200       return -1;
201     }  
202
203     arg = 1;
204     if(ioctl(dev->fd_, TUNSIFHEAD, &arg) < 0) {
205       log_printf(ERROR, "can't enable multi-af mode for interface: %s", strerror(errno));
206       return -1;
207     }  
208
209     arg = IFF_BROADCAST;
210     arg |= IFF_MULTICAST;
211     if(ioctl(dev->fd_, TUNSIFMODE, &arg) < 0) {
212       log_printf(ERROR, "can't enable multicast for interface: %s", strerror(errno));
213       return -1;
214     }  
215   }
216
217   return 0;
218 }
219
220 #elif defined(__GNUC__) && defined(__NetBSD__)
221  #warning this device has never been tested on NetBSD and might not work
222 int tun_init_post(tun_device_t* dev)
223 {
224   if(!dev)
225     return -1;
226
227   dev->with_pi_ = 0;
228
229   int arg = IFF_POINTOPOINT|IFF_MULTICAST;
230   ioctl(dev->fd_, TUNSIFMODE, &arg);
231   arg = 0;
232   ioctl(dev->fd_, TUNSLMODE, &arg);
233
234   return 0;
235 }
236
237 #else
238  #error This Device works just for OpenBSD, FreeBSD or NetBSD
239 #endif
240
241
242
243 void tun_close(tun_device_t* dev)
244 {
245   if(!dev)
246     return;
247
248   if(dev->fd_ > 0)
249     close(dev->fd_);
250
251   if(dev->actual_name_)
252     free(dev->actual_name_);
253
254   if(dev->net_addr_)
255     free(dev->net_addr_);
256
257   if(dev->net_mask_)
258     free(dev->net_mask_);
259 }
260
261 int tun_read(tun_device_t* dev, u_int8_t* buf, u_int32_t len)
262 {
263   if(!dev || dev->fd_ < 0)
264     return -1;
265
266   if(dev->with_pi_)
267   {
268     struct iovec iov[2];
269     u_int32_t type;
270     
271     iov[0].iov_base = &type;
272     iov[0].iov_len = sizeof(type);
273     iov[1].iov_base = buf;
274     iov[1].iov_len = len;
275     return(tun_fix_return(readv(dev->fd_, iov, 2), sizeof(type)));
276   }
277   else
278     return(read(dev->fd_, buf, len));
279 }
280
281 int tun_write(tun_device_t* dev, u_int8_t* buf, u_int32_t len)
282 {
283   if(!dev || dev->fd_ < 0)
284     return -1;
285
286   if(!buf)
287     return 0;
288
289   if(dev->with_pi_)
290   {
291     struct iovec iov[2];
292     u_int32_t type;
293     struct ip *hdr = (struct ip*)buf;
294     
295     type = 0;
296     if(hdr->ip_v == 4)
297       type = htonl(AF_INET);
298     else
299       type = htonl(AF_INET6);
300     
301     iov[0].iov_base = &type;
302     iov[0].iov_len = sizeof(type);
303     iov[1].iov_base = buf;
304     iov[1].iov_len = len;
305     return(tun_fix_return(writev(dev->fd_, iov, 2), sizeof(type)));
306   }
307   else
308     return(write(dev->fd_, buf, len));
309 }
310
311 void tun_do_ifconfig(tun_device_t* dev)
312 {
313   if(!dev || !dev->actual_name_ || !dev->net_addr_ || !dev->net_mask_)
314     return;
315
316
317   char* command = NULL;
318   char* netmask;
319   char* end;
320   if(dev->type_ == TYPE_TAP) {
321 #if defined(__GNUC__) && defined(__OpenBSD__)
322     end = " link0";
323 #elif defined(__GNUC__) && defined(__FreeBSD__)
324     end = " up";
325 #elif defined(__GNUC__) && defined(__NetBSD__)
326     end = "";
327 #else
328  #error This Device works just for OpenBSD, FreeBSD or NetBSD
329 #endif
330   }
331   else
332     end = " up";
333
334   asprintf(&command, "/sbin/ifconfig %s %s netmask %s mtu %d%s", dev->actual_name_, dev->net_addr_,
335                                                                  dev->net_mask_, dev->mtu_, end);
336   if(!command) {
337     log_printf(ERROR, "Execution of ifconfig failed");
338     return;
339   }
340
341   int result = system(command);
342   if(result == -1)
343     log_printf(ERROR, "Execution of ifconfig failed");
344   else
345     log_printf(NOTICE, "ifconfig returned %d", WEXITSTATUS(result));
346
347   free(command);
348 }