1 |
/* |
2 |
device.c -- Interaction BSD tun/tap device |
3 |
Copyright (C) 2001-2004 Ivo Timmermans <ivo@tinc-vpn.org>, |
4 |
2001-2004 Guus Sliepen <guus@tinc-vpn.org> |
5 |
|
6 |
This program is free software; you can redistribute it and/or modify |
7 |
it under the terms of the GNU General Public License as published by |
8 |
the Free Software Foundation; either version 2 of the License, or |
9 |
(at your option) any later version. |
10 |
|
11 |
This program is distributed in the hope that it will be useful, |
12 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
GNU General Public License for more details. |
15 |
|
16 |
You should have received a copy of the GNU General Public License |
17 |
along with this program; if not, write to the Free Software |
18 |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
19 |
|
20 |
$Id: device.c 1398 2004-11-01 15:18:53Z guus $ |
21 |
*/ |
22 |
|
23 |
#define DEFAULT_DEVICE "/dev/tun0" |
24 |
|
25 |
typedef enum device_type { |
26 |
DEVICE_TYPE_TUN, |
27 |
DEVICE_TYPE_TUNIFHEAD, |
28 |
DEVICE_TYPE_TAP, |
29 |
} device_type_t; |
30 |
|
31 |
int device_fd = -1; |
32 |
char *device; |
33 |
char *iface; |
34 |
char *device_info; |
35 |
static int device_total_in = 0; |
36 |
static int device_total_out = 0; |
37 |
#ifdef HAVE_OPENBSD |
38 |
static device_type_t device_type = DEVICE_TYPE_TUNIFHEAD; |
39 |
#else |
40 |
static device_type_t device_type = DEVICE_TYPE_TUN; |
41 |
#endif |
42 |
|
43 |
bool setup_device(void) { |
44 |
char *type; |
45 |
|
46 |
cp(); |
47 |
|
48 |
if(!get_config_string(lookup_config(config_tree, "Device"), &device)) |
49 |
device = DEFAULT_DEVICE; |
50 |
|
51 |
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) |
52 |
iface = rindex(device, '/') ? rindex(device, '/') + 1 : device; |
53 |
|
54 |
if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0) { |
55 |
logger(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno)); |
56 |
return false; |
57 |
} |
58 |
|
59 |
if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) { |
60 |
if(!strcasecmp(type, "tun")) |
61 |
/* use default */; |
62 |
else if(!strcasecmp(type, "tunnohead")) |
63 |
device_type = DEVICE_TYPE_TUN; |
64 |
else if(!strcasecmp(type, "tunifhead")) |
65 |
device_type = DEVICE_TYPE_TUNIFHEAD; |
66 |
else if(!strcasecmp(type, "tap")) |
67 |
device_type = DEVICE_TYPE_TAP; |
68 |
else { |
69 |
logger(LOG_ERR, _("Unknown device type %s!"), type); |
70 |
return false; |
71 |
} |
72 |
} else { |
73 |
if(strstr(device, "tap")) |
74 |
device_type = DEVICE_TYPE_TAP; |
75 |
} |
76 |
|
77 |
switch(device_type) { |
78 |
default: |
79 |
device_type = DEVICE_TYPE_TUN; |
80 |
case DEVICE_TYPE_TUN: |
81 |
#ifdef TUNSIFHEAD |
82 |
{ |
83 |
const int zero = 0; |
84 |
if(ioctl(device_fd, TUNSIFHEAD, &zero, sizeof zero) == -1) { |
85 |
logger(LOG_ERR, _("System call `%s' failed: %s"), "ioctl", strerror(errno)); |
86 |
return false; |
87 |
} |
88 |
} |
89 |
#endif |
90 |
#if defined(TUNSIFMODE) && defined(IFF_BROADCAST) && defined(IFF_MULTICAST) |
91 |
{ |
92 |
const int mode = IFF_BROADCAST | IFF_MULTICAST; |
93 |
ioctl(device_fd, TUNSIFMODE, &mode, sizeof mode); |
94 |
} |
95 |
#endif |
96 |
|
97 |
device_info = _("Generic BSD tun device"); |
98 |
break; |
99 |
case DEVICE_TYPE_TUNIFHEAD: |
100 |
#ifdef TUNSIFHEAD |
101 |
{ |
102 |
const int one = 1; |
103 |
if(ioctl(device_fd, TUNSIFHEAD, &one, sizeof one) == -1) { |
104 |
logger(LOG_ERR, _("System call `%s' failed: %s"), "ioctl", strerror(errno)); |
105 |
return false; |
106 |
} |
107 |
} |
108 |
#endif |
109 |
#if defined(TUNSIFMODE) && defined(IFF_BROADCAST) && defined(IFF_MULTICAST) |
110 |
{ |
111 |
const int mode = IFF_BROADCAST | IFF_MULTICAST; |
112 |
ioctl(device_fd, TUNSIFMODE, &mode, sizeof mode); |
113 |
} |
114 |
#endif |
115 |
|
116 |
device_info = _("Generic BSD tun device"); |
117 |
break; |
118 |
case DEVICE_TYPE_TAP: |
119 |
if(routing_mode == RMODE_ROUTER) |
120 |
overwrite_mac = true; |
121 |
device_info = _("Generic BSD tap device"); |
122 |
break; |
123 |
} |
124 |
|
125 |
logger(LOG_INFO, _("%s is a %s"), device, device_info); |
126 |
|
127 |
return true; |
128 |
} |
129 |
|
130 |
void close_device(void) { |
131 |
cp(); |
132 |
|
133 |
close(device_fd); |
134 |
} |
135 |
|
136 |
bool read_packet(vpn_packet_t *packet) { |
137 |
int lenin; |
138 |
|
139 |
cp(); |
140 |
|
141 |
switch(device_type) { |
142 |
case DEVICE_TYPE_TUN: |
143 |
if((lenin = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { |
144 |
logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, |
145 |
device, strerror(errno)); |
146 |
return false; |
147 |
} |
148 |
|
149 |
switch(packet->data[14] >> 4) { |
150 |
case 4: |
151 |
packet->data[12] = 0x08; |
152 |
packet->data[13] = 0x00; |
153 |
break; |
154 |
case 6: |
155 |
packet->data[12] = 0x86; |
156 |
packet->data[13] = 0xDD; |
157 |
break; |
158 |
default: |
159 |
ifdebug(TRAFFIC) logger(LOG_ERR, |
160 |
_ ("Unknown IP version %d while reading packet from %s %s"), |
161 |
packet->data[14] >> 4, device_info, device); |
162 |
return false; |
163 |
} |
164 |
|
165 |
packet->len = lenin + 14; |
166 |
break; |
167 |
|
168 |
case DEVICE_TYPE_TUNIFHEAD: { |
169 |
u_int32_t type; |
170 |
struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, MTU - 14}}; |
171 |
|
172 |
if((lenin = readv(device_fd, vector, 2)) <= 0) { |
173 |
logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, |
174 |
device, strerror(errno)); |
175 |
return false; |
176 |
} |
177 |
|
178 |
switch (ntohl(type)) { |
179 |
case AF_INET: |
180 |
packet->data[12] = 0x08; |
181 |
packet->data[13] = 0x00; |
182 |
break; |
183 |
|
184 |
case AF_INET6: |
185 |
packet->data[12] = 0x86; |
186 |
packet->data[13] = 0xDD; |
187 |
break; |
188 |
|
189 |
default: |
190 |
ifdebug(TRAFFIC) logger(LOG_ERR, |
191 |
_ ("Unknown address family %x while reading packet from %s %s"), |
192 |
ntohl(type), device_info, device); |
193 |
return false; |
194 |
} |
195 |
|
196 |
packet->len = lenin + 10; |
197 |
break; |
198 |
} |
199 |
|
200 |
case DEVICE_TYPE_TAP: |
201 |
if((lenin = read(device_fd, packet->data, MTU)) <= 0) { |
202 |
logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, |
203 |
device, strerror(errno)); |
204 |
return false; |
205 |
} |
206 |
|
207 |
packet->len = lenin; |
208 |
break; |
209 |
|
210 |
default: |
211 |
return false; |
212 |
} |
213 |
|
214 |
device_total_in += packet->len; |
215 |
|
216 |
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Read packet of %d bytes from %s"), |
217 |
packet->len, device_info); |
218 |
|
219 |
return true; |
220 |
} |
221 |
|
222 |
bool write_packet(vpn_packet_t *packet) |
223 |
{ |
224 |
cp(); |
225 |
|
226 |
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"), |
227 |
packet->len, device_info); |
228 |
|
229 |
switch(device_type) { |
230 |
case DEVICE_TYPE_TUN: |
231 |
if(write(device_fd, packet->data + 14, packet->len - 14) < 0) { |
232 |
logger(LOG_ERR, _("Error while writing to %s %s: %s"), device_info, |
233 |
device, strerror(errno)); |
234 |
return false; |
235 |
} |
236 |
break; |
237 |
|
238 |
case DEVICE_TYPE_TUNIFHEAD: { |
239 |
u_int32_t type; |
240 |
struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, MTU - 14}}; |
241 |
int af; |
242 |
|
243 |
af = (packet->data[12] << 8) + packet->data[13]; |
244 |
|
245 |
switch (af) { |
246 |
case 0x0800: |
247 |
type = htonl(AF_INET); |
248 |
break; |
249 |
case 0x86DD: |
250 |
type = htonl(AF_INET6); |
251 |
break; |
252 |
default: |
253 |
ifdebug(TRAFFIC) logger(LOG_ERR, |
254 |
_("Unknown address family %x while writing packet to %s %s"), |
255 |
af, device_info, device); |
256 |
return false; |
257 |
} |
258 |
|
259 |
if(writev(device_fd, vector, 2) < 0) { |
260 |
logger(LOG_ERR, _("Can't write to %s %s: %s"), device_info, device, |
261 |
strerror(errno)); |
262 |
return false; |
263 |
} |
264 |
break; |
265 |
} |
266 |
|
267 |
case DEVICE_TYPE_TAP: |
268 |
if(write(device_fd, packet->data, packet->len) < 0) { |
269 |
logger(LOG_ERR, _("Error while writing to %s %s: %s"), device_info, |
270 |
device, strerror(errno)); |
271 |
return false; |
272 |
} |
273 |
break; |
274 |
|
275 |
default: |
276 |
return false; |
277 |
} |
278 |
|
279 |
device_total_out += packet->len; |
280 |
|
281 |
return true; |
282 |
} |
283 |
|
284 |
void dump_device_stats(void) |
285 |
{ |
286 |
cp(); |
287 |
|
288 |
logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device); |
289 |
logger(LOG_DEBUG, _(" total bytes in: %10d"), device_total_in); |
290 |
logger(LOG_DEBUG, _(" total bytes out: %10d"), device_total_out); |
291 |
} |