Imported Upstream version 0.3.5
[anytun.git] / src / bsd / tunDevice.cpp
1 /*
2  *  anytun
3  *
4  *  The secure anycast tunneling protocol (satp) defines a protocol used
5  *  for communication between any combination of unicast and anycast
6  *  tunnel endpoints.  It has less protocol overhead than IPSec in Tunnel
7  *  mode and allows tunneling of every ETHER TYPE protocol (e.g.
8  *  ethernet, ip, arp ...). satp directly includes cryptography and
9  *  message authentication based on the methods used by SRTP.  It is
10  *  intended to deliver a generic, scaleable and secure solution for
11  *  tunneling and relaying of packets of any protocol.
12  *
13  *
14  *  Copyright (C) 2007-2014 Markus Grüneis, Othmar Gsenger, Erwin Nindl,
15  *                          Christian Pointner <satp@wirdorange.org>
16  *
17  *  This file is part of Anytun.
18  *
19  *  Anytun is free software: you can redistribute it and/or modify
20  *  it under the terms of the GNU General Public License as published by
21  *  the Free Software Foundation, either version 3 of the License, or
22  *  any later version.
23  *
24  *  Anytun is distributed in the hope that it will be useful,
25  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
26  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  *  GNU General Public License for more details.
28  *
29  *  You should have received a copy of the GNU General Public License
30  *  along with Anytun.  If not, see <http://www.gnu.org/licenses/>.
31  *
32  *  In addition, as a special exception, the copyright holders give
33  *  permission to link the code of portions of this program with the
34  *  OpenSSL library under certain conditions as described in each
35  *  individual source file, and distribute linked combinations
36  *  including the two.
37  *  You must obey the GNU General Public License in all respects
38  *  for all of the code used other than OpenSSL.  If you modify
39  *  file(s) with this exception, you may extend this exception to your
40  *  version of the file(s), but you are not obligated to do so.  If you
41  *  do not wish to do so, delete this exception statement from your
42  *  version.  If you delete this exception statement from all source
43  *  files in the program, then also delete it here.
44  */
45
46 #include <sstream>
47 #include <boost/assign.hpp>
48
49 #include <fcntl.h>
50 #include <unistd.h>
51 #include <errno.h>
52 #include <sys/socket.h>
53 #include <net/if.h>
54 #include <net/if_tun.h>
55 #include <sys/ioctl.h>
56 #include <sys/types.h>
57 #include <sys/uio.h>
58 #include <netinet/in_systm.h>
59 #include <netinet/in.h>
60 #include <netinet/ip.h>
61
62 #include "tunDevice.h"
63 #include "threadUtils.hpp"
64 #include "log.h"
65 #include "anytunError.h"
66 #include "sysExec.h"
67
68 #define DEVICE_FILE_MAX 255
69
70 TunDevice::TunDevice(std::string dev_name, std::string dev_type, std::string ifcfg_addr, uint16_t ifcfg_prefix) : conf_(dev_name, dev_type, ifcfg_addr, ifcfg_prefix, 1400),sys_exec_(NULL)
71 {
72   std::string device_file = "/dev/";
73   bool dynamic = true;
74   if(dev_name != "") {
75     device_file.append(dev_name);
76     dynamic = false;
77   }
78 #if defined(__GNUC__) && defined(__OpenBSD__)
79   else if(conf_.type_ == TYPE_TUN || conf_.type_ == TYPE_TAP) {
80     device_file.append("tun");
81     actual_name_ = "tun";
82   }
83 #else
84   else if(conf_.type_ == TYPE_TUN) {
85     device_file.append("tun");
86     actual_name_ = "tun";
87   } else if(conf_.type_ == TYPE_TAP) {
88     device_file.append("tap");
89     actual_name_ = "tap";
90   }
91 #endif
92   else {
93     AnytunError::throwErr() << "unable to recognize type of device (tun or tap)";
94   }
95
96   uint32_t dev_id=0;
97   if(dynamic) {
98     for(; dev_id <= DEVICE_FILE_MAX; ++dev_id) {
99       std::ostringstream ds;
100       ds << device_file;
101       ds << dev_id;
102       fd_ = ::open(ds.str().c_str(), O_RDWR);
103       if(fd_ >= 0) {
104         break;
105       }
106     }
107   } else {
108     fd_ = ::open(device_file.c_str(), O_RDWR);
109   }
110
111   if(fd_ < 0) {
112     if(dynamic) {
113       AnytunError::throwErr() << "can't open device file dynamically: no unused node left";
114     } else {
115       AnytunError::throwErr() << "can't open device file (" << device_file << "): " << AnytunErrno(errno);
116     }
117   }
118
119   if(dynamic) {
120     std::stringstream s;
121     s << actual_name_;
122     s << dev_id;
123     actual_name_ = s.str();
124   } else {
125     actual_name_ = dev_name;
126   }
127
128   actual_node_ = device_file;
129
130   init_post();
131
132   if(ifcfg_addr != "") {
133     do_ifconfig();
134   }
135 }
136
137 TunDevice::~TunDevice()
138 {
139   if(fd_ > 0) {
140     ::close(fd_);
141   }
142 }
143
144 #if defined(__GNUC__) && defined(__OpenBSD__)
145
146 void TunDevice::init_post()
147 {
148   with_pi_ = true;
149   if(conf_.type_ == TYPE_TAP) {
150     with_pi_ = false;
151   }
152
153   struct tuninfo ti;
154
155   if(ioctl(fd_, TUNGIFINFO, &ti) < 0) {
156     ::close(fd_);
157     AnytunError::throwErr() << "can't enable multicast for interface: " << AnytunErrno(errno);
158   }
159
160   ti.flags |= IFF_MULTICAST;
161   if(conf_.type_ == TYPE_TUN) {
162     ti.flags &= ~IFF_POINTOPOINT;
163   }
164
165   if(ioctl(fd_, TUNSIFINFO, &ti) < 0) {
166     ::close(fd_);
167     AnytunError::throwErr() << "can't enable multicast for interface: " << AnytunErrno(errno);
168   }
169 }
170
171 #elif defined(__GNUC__) && (defined(__FreeBSD__) || defined(__FreeBSD_kernel__))
172
173 void TunDevice::init_post()
174 {
175   with_pi_ = true;
176   if(conf_.type_ == TYPE_TAP) {
177     with_pi_ = false;
178   }
179
180   if(conf_.type_ == TYPE_TUN) {
181     int arg = 0;
182     if(ioctl(fd_, TUNSLMODE, &arg) < 0) {
183       ::close(fd_);
184       AnytunError::throwErr() << "can't disable link-layer mode for interface: " << AnytunErrno(errno);
185     }
186
187     arg = 1;
188     if(ioctl(fd_, TUNSIFHEAD, &arg) < 0) {
189       ::close(fd_);
190       AnytunError::throwErr() << "can't enable multi-af modefor interface: " << AnytunErrno(errno);
191     }
192
193     arg = IFF_BROADCAST;
194     arg |= IFF_MULTICAST;
195     if(ioctl(fd_, TUNSIFMODE, &arg) < 0) {
196       ::close(fd_);
197       AnytunError::throwErr() << "can't enable multicast for interface: " << AnytunErrno(errno);
198     }
199   }
200 }
201
202 #elif defined(__GNUC__) && defined(__NetBSD__)
203
204 void TunDevice::init_post()
205 {
206   with_pi_ = false;
207
208   int arg = IFF_POINTOPOINT|IFF_MULTICAST;
209   ioctl(fd_, TUNSIFMODE, &arg);
210   arg = 0;
211   ioctl(fd_, TUNSLMODE, &arg);
212 }
213
214 #else
215 #error This Device works just for OpenBSD, FreeBSD or NetBSD
216 #endif
217
218 int TunDevice::fix_return(int ret, size_t pi_length) const
219 {
220   if(ret < 0) {
221     return ret;
222   }
223
224   return (static_cast<size_t>(ret) > pi_length ? (ret - pi_length) : 0);
225 }
226
227 int TunDevice::read(uint8_t* buf, uint32_t len)
228 {
229   if(fd_ < 0) {
230     return -1;
231   }
232
233   if(with_pi_) {
234     struct iovec iov[2];
235     uint32_t type;
236
237     iov[0].iov_base = &type;
238     iov[0].iov_len = sizeof(type);
239     iov[1].iov_base = buf;
240     iov[1].iov_len = len;
241     return(fix_return(::readv(fd_, iov, 2), sizeof(type)));
242   } else {
243     return(::read(fd_, buf, len));
244   }
245 }
246
247 int TunDevice::write(uint8_t* buf, uint32_t len)
248 {
249   if(fd_ < 0) {
250     return -1;
251   }
252
253   if(!buf) {
254     return 0;
255   }
256
257   if(with_pi_) {
258     struct iovec iov[2];
259     uint32_t type;
260     struct ip* hdr = reinterpret_cast<struct ip*>(buf);
261
262     type = 0;
263     if(hdr->ip_v == 4) {
264       type = htonl(AF_INET);
265     } else {
266       type = htonl(AF_INET6);
267     }
268
269     iov[0].iov_base = &type;
270     iov[0].iov_len = sizeof(type);
271     iov[1].iov_base = buf;
272     iov[1].iov_len = len;
273     return(fix_return(::writev(fd_, iov, 2), sizeof(type)));
274   } else {
275     return(::write(fd_, buf, len));
276   }
277 }
278
279 void TunDevice::do_ifconfig()
280 {
281   std::ostringstream mtu_ss;
282   mtu_ss << conf_.mtu_;
283   StringVector args = boost::assign::list_of(actual_name_)(conf_.addr_.toString())("netmask")(conf_.netmask_.toString())("mtu")(mtu_ss.str());
284
285   if(conf_.type_ == TYPE_TUN) {
286     args.push_back("up");
287   } else {
288 #if defined(__GNUC__) && defined(__OpenBSD__)
289     args.push_back("link0");
290 #elif defined(__GNUC__) && (defined(__FreeBSD__) || defined(__FreeBSD_kernel__))
291     args.push_back("up");
292 #elif defined(__GNUC__) && defined(__NetBSD__)
293     // nothing to be done here
294 #else
295 #error This Device works just for OpenBSD, FreeBSD or NetBSD
296 #endif
297   }
298   sys_exec_ = new SysExec("/sbin/ifconfig", args);
299 }
300
301 void TunDevice::waitUntilReady()
302 {
303   if(sys_exec_) {
304     SysExec::waitAndDestroy(sys_exec_);
305   }
306 }
307