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