Imported Upstream version 0.3.3
[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 methodes 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-2009 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
33 #include <sstream>
34 #include <boost/assign.hpp>
35
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <errno.h>
39 #include <sys/socket.h>
40 #include <net/if.h>
41 #include <net/if_tun.h>
42 #include <sys/ioctl.h>
43 #include <sys/types.h>
44 #include <sys/uio.h>
45 #include <netinet/in_systm.h>
46 #include <netinet/in.h>
47 #include <netinet/ip.h>
48
49 #include "tunDevice.h"
50 #include "threadUtils.hpp"
51 #include "log.h"
52 #include "anytunError.h"
53 #include "sysExec.h"
54
55 #define DEVICE_FILE_MAX 255
56
57 TunDevice::TunDevice(std::string dev_name, std::string dev_type, std::string ifcfg_addr, u_int16_t ifcfg_prefix) : conf_(dev_name, dev_type, ifcfg_addr, ifcfg_prefix, 1400),sys_exec_(NULL)
58 {
59   std::string device_file = "/dev/";
60   bool dynamic = true;
61   if(dev_name != "") {
62     device_file.append(dev_name);
63     dynamic = false;
64   }
65 #if defined(__GNUC__) && defined(__OpenBSD__)
66   else if(conf_.type_ == TYPE_TUN || conf_.type_ == TYPE_TAP) {
67     device_file.append("tun");
68     actual_name_ = "tun";
69   }
70 #else
71   else if(conf_.type_ == TYPE_TUN) {
72     device_file.append("tun");
73     actual_name_ = "tun";
74   }
75   else if(conf_.type_ == TYPE_TAP) {
76     device_file.append("tap");
77     actual_name_ = "tap";
78   }
79 #endif
80   else
81     AnytunError::throwErr() << "unable to recognize type of device (tun or tap)";
82
83   u_int32_t dev_id=0;
84   if(dynamic) {
85     for(; dev_id <= DEVICE_FILE_MAX; ++dev_id) {
86       std::ostringstream ds;
87       ds << device_file;
88       ds << dev_id;
89       fd_ = ::open(ds.str().c_str(), O_RDWR);
90       if(fd_ >= 0)
91         break;
92     }
93   }
94   else
95     fd_ = ::open(device_file.c_str(), O_RDWR);
96
97   if(fd_ < 0) {
98     if(dynamic)
99       AnytunError::throwErr() << "can't open device file dynamically: no unused node left";
100     else
101       AnytunError::throwErr() << "can't open device file (" << device_file << "): " << AnytunErrno(errno);
102   }
103
104   if(dynamic) {
105     std::stringstream s;
106     s << actual_name_;
107     s << dev_id;
108     actual_name_ = s.str();
109   }
110   else
111     actual_name_ = dev_name;
112   
113   actual_node_ = device_file;
114
115   init_post();
116
117   if(ifcfg_addr != "")
118     do_ifconfig();
119 }
120
121 TunDevice::~TunDevice()
122 {
123   if(fd_ > 0)
124     ::close(fd_);
125 }
126
127 #if defined(__GNUC__) && defined(__OpenBSD__)
128
129 void TunDevice::init_post()
130 {
131   with_pi_ = true;
132   if(conf_.type_ == TYPE_TAP)
133     with_pi_ = false;
134   
135   struct tuninfo ti;  
136
137   if (ioctl(fd_, TUNGIFINFO, &ti) < 0) {
138     ::close(fd_);
139     AnytunError::throwErr() << "can't enable multicast for interface: " << AnytunErrno(errno);
140   }
141   
142   ti.flags |= IFF_MULTICAST;
143   if(conf_.type_ == TYPE_TUN)
144     ti.flags &= ~IFF_POINTOPOINT;
145   
146   if (ioctl(fd_, TUNSIFINFO, &ti) < 0) {
147     ::close(fd_);
148     AnytunError::throwErr() << "can't enable multicast for interface: " << AnytunErrno(errno);
149   }
150 }
151
152 #elif defined(__GNUC__) && (defined(__FreeBSD__) || defined(__FreeBSD_kernel__))
153
154 void TunDevice::init_post()
155 {
156   with_pi_ = true;
157   if(conf_.type_ == TYPE_TAP)
158     with_pi_ = false;
159
160   if(conf_.type_ == TYPE_TUN) {
161     int arg = 0;
162     if(ioctl(fd_, TUNSLMODE, &arg) < 0) {
163       ::close(fd_);
164       AnytunError::throwErr() << "can't disable link-layer mode for interface: " << AnytunErrno(errno);
165     }
166
167     arg = 1;
168     if(ioctl(fd_, TUNSIFHEAD, &arg) < 0) {
169       ::close(fd_);
170       AnytunError::throwErr() << "can't enable multi-af modefor interface: " << AnytunErrno(errno);
171     }
172
173     arg = IFF_BROADCAST;
174     arg |= IFF_MULTICAST;
175     if(ioctl(fd_, TUNSIFMODE, &arg) < 0) {
176       ::close(fd_);
177       AnytunError::throwErr() << "can't enable multicast for interface: " << AnytunErrno(errno);
178     }
179   }
180 }
181
182 #elif defined(__GNUC__) && defined(__NetBSD__)
183
184 void TunDevice::init_post()
185 {
186   with_pi_ = false;
187
188   int arg = IFF_POINTOPOINT|IFF_MULTICAST;
189   ioctl(fd_, TUNSIFMODE, &arg);
190   arg = 0;
191   ioctl(fd_, TUNSLMODE, &arg);
192 }
193
194 #else
195  #error This Device works just for OpenBSD, FreeBSD or NetBSD
196 #endif
197
198 int TunDevice::fix_return(int ret, size_t pi_length) const
199 {
200   if(ret < 0)
201     return ret;
202
203   return (static_cast<size_t>(ret) > pi_length ? (ret - pi_length) : 0);
204 }
205
206 int TunDevice::read(u_int8_t* buf, u_int32_t len)
207 {
208   if(fd_ < 0)
209     return -1;
210   
211   if(with_pi_) {
212     struct iovec iov[2];
213     u_int32_t type;
214     
215     iov[0].iov_base = &type;
216     iov[0].iov_len = sizeof(type);
217     iov[1].iov_base = buf;
218     iov[1].iov_len = len;
219     return(fix_return(::readv(fd_, iov, 2), sizeof(type)));
220   }
221   else
222     return(::read(fd_, buf, len));
223 }
224
225 int TunDevice::write(u_int8_t* buf, u_int32_t len)
226 {
227   if(fd_ < 0)
228     return -1;
229   
230   if(!buf)
231     return 0;
232
233   if(with_pi_) {
234     struct iovec iov[2];
235     u_int32_t type;
236     struct ip *hdr = reinterpret_cast<struct ip*>(buf);
237     
238     type = 0;
239     if(hdr->ip_v == 4)
240       type = htonl(AF_INET);
241     else
242       type = htonl(AF_INET6);
243     
244     iov[0].iov_base = &type;
245     iov[0].iov_len = sizeof(type);
246     iov[1].iov_base = buf;
247     iov[1].iov_len = len;
248     return(fix_return(::writev(fd_, iov, 2), sizeof(type)));
249   }
250   else
251     return(::write(fd_, buf, len));
252 }
253
254 void TunDevice::do_ifconfig()
255 {
256   std::ostringstream mtu_ss;
257   mtu_ss << conf_.mtu_;
258   StringVector args = boost::assign::list_of(actual_name_)(conf_.addr_.toString())("netmask")(conf_.netmask_.toString())("mtu")(mtu_ss.str());
259
260   if(conf_.type_ == TYPE_TUN)
261     args.push_back("up");
262   else {
263 #if defined(__GNUC__) && defined(__OpenBSD__)
264     args.push_back("link0");
265 #elif defined(__GNUC__) && (defined(__FreeBSD__) || defined(__FreeBSD_kernel__))
266     args.push_back("up");
267 #elif defined(__GNUC__) && defined(__NetBSD__)
268         // nothing to be done here
269 #else
270  #error This Device works just for OpenBSD, FreeBSD or NetBSD
271 #endif
272   }
273   sys_exec_ = new SysExec("/sbin/ifconfig", args);
274 }
275
276 void TunDevice::waitUntilReady()
277 {
278   if(sys_exec_)
279     SysExec::waitAndDestroy(sys_exec_);
280 }
281