Update support for Solaris.
[oweals/tinc.git] / src / solaris / device.c
1 /*
2     device.c -- Interaction with Solaris tun device
3     Copyright (C) 2001-2005 Ivo Timmermans,
4                   2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
5                   2001-2013 Guus Sliepen <guus@tinc-vpn.org>
6
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License along
18     with this program; if not, write to the Free Software Foundation, Inc.,
19     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22
23 #include "../system.h"
24
25 #include <sys/stropts.h>
26 #include <sys/sockio.h>
27
28 #include "../conf.h"
29 #include "../device.h"
30 #include "../logger.h"
31 #include "../net.h"
32 #include "../route.h"
33 #include "../utils.h"
34 #include "../xalloc.h"
35
36 #define DEFAULT_TUN_DEVICE "/dev/tun"
37 #define DEFAULT_TAP_DEVICE "/dev/tap"
38
39 static enum {
40         DEVICE_TYPE_TUN,
41         DEVICE_TYPE_TAP,
42 } device_type = DEVICE_TYPE_TUN;
43
44 int device_fd = -1;
45 static int if_fd = -1;
46 static int ip_fd = -1;
47 static int arp_fd = -1;
48 char *device = NULL;
49 char *iface = NULL;
50 static char *device_info = NULL;
51
52 uint64_t device_total_in = 0;
53 uint64_t device_total_out = 0;
54
55 static bool setup_device(void) {
56         char *type;
57
58         if(!get_config_string(lookup_config(config_tree, "Device"), &device)) {
59                 if(routing_mode == RMODE_ROUTER)
60                         device = xstrdup(DEFAULT_TUN_DEVICE);
61                 else
62                         device = xstrdup(DEFAULT_TAP_DEVICE);
63         }
64
65         if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) {
66                 if(!strcasecmp(type, "tun"))
67                         /* use default */;
68                 else if(!strcasecmp(type, "tap"))
69                         device_type = DEVICE_TYPE_TAP;
70                 else {
71                         logger(LOG_ERR, "Unknown device type %s!", type);
72                         return false;
73                 }
74         } else {
75                 if(strstr(device, "tap") || routing_mode != RMODE_ROUTER)
76                         device_type = DEVICE_TYPE_TAP;
77         }
78
79         if(device_type == DEVICE_TYPE_TUN)
80                 device_info = "Solaris tun device";
81         else
82                 device_info = "Solaris tap device";
83
84         /* The following is black magic copied from OpenVPN. */
85
86         if((ip_fd = open("/dev/ip", O_RDWR, 0)) < 0) {
87                 logger(LOG_ERR, "Could not open %s: %s\n", "/dev/ip", strerror(errno));
88                 return false;
89         }
90
91         if((device_fd = open(device, O_RDWR, 0)) < 0) {
92                 logger(LOG_ERR, "Could not open %s: %s\n", device, strerror(errno));
93                 return false;
94         }
95
96         /* Get unit number. */
97
98         char *ptr = device;
99         get_config_string(lookup_config(config_tree, "Interface"), &ptr);
100
101         while(*ptr && !isdigit(*ptr))
102                 ptr++;
103         int ppa = atoi(ptr);
104
105         /* Assign a new PPA and get its unit number. */
106
107         struct strioctl strioc_ppa = {
108                 .ic_cmd = TUNNEWPPA,
109                 .ic_len = sizeof ppa,
110                 .ic_dp = (char *)&ppa,
111         };
112
113         if(!*ptr) { /* no number given, try dynamic */
114                 bool found = false;
115                 while(!found && ppa < 64) {
116                         int new_ppa = ioctl(device_fd, I_STR, &strioc_ppa);
117                         if(new_ppa >= 0) {
118                                 ppa = new_ppa;
119                                 found = true;
120                                 break;
121                         }
122                         ppa++;
123                 }
124                 if(!found) {
125                         logger(LOG_ERR, "Could not find free PPA for %s %s!", device_info, device);
126                         return false;
127                 }
128         } else { /* try this particular one */
129                 if((ppa = ioctl(device_fd, I_STR, &strioc_ppa)) < 0) {
130                         logger(LOG_ERR, "Could not assign PPA %d for %s %s!", ppa, device_info, device);
131                         return false;
132                 }
133         }
134
135         if((if_fd = open(device, O_RDWR, 0)) < 0) {
136                 logger(LOG_ERR, "Could not open %s: %s\n", device, strerror(errno));
137                 return false;
138         }
139
140         if(ioctl(if_fd, I_PUSH, "ip") < 0) {
141                 logger(LOG_ERR, "Could not push IP module onto %s %s!", device_info, device);
142                 return false;
143         }
144
145         xasprintf(&iface, "%s%d", device_type == DEVICE_TYPE_TUN ? "tun" : "tap", ppa);
146
147         {
148                 /* Remove muxes just in case they are left over from a crashed tincd */
149                 struct lifreq ifr = {};
150                 strncpy(ifr.lifr_name, iface, sizeof ifr.lifr_name);
151                 if(ioctl(ip_fd, SIOCGLIFMUXID, &ifr) >= 0) {
152                         int muxid = ifr.lifr_arp_muxid;
153                         ioctl(ip_fd, I_PUNLINK, muxid);
154                         muxid = ifr.lifr_ip_muxid;
155                         ioctl(ip_fd, I_PUNLINK, muxid);
156                 }
157         }
158
159         if(device_type == DEVICE_TYPE_TUN) {
160                 /* Assign ppa according to the unit number returned by tun device */
161                 if(ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0) {
162                         logger(LOG_ERR, "Could not set PPA %d on %s %s!", ppa, device_info, device);
163                         return false;
164                 }
165         }
166
167         int arp_fd = -1;
168
169         if(device_type == DEVICE_TYPE_TAP) {
170                 struct lifreq ifr = {};
171
172                 if(ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) {
173                         logger(LOG_ERR, "Could not set flags on %s %s!", device_info, device);
174                         return false;
175                 }
176
177                 strncpy(ifr.lifr_name, iface, sizeof(ifr.lifr_name));
178                 ifr.lifr_ppa = ppa;
179
180                 /* Assign ppa according to the unit number returned by tun device */
181                 if(ioctl(if_fd, SIOCSLIFNAME, &ifr) < 0) {
182                         logger(LOG_ERR, "Could not set PPA %d on %s %s!", ppa, device_info, device);
183                         return false;
184                 }
185                 if(ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) {
186                         logger(LOG_ERR, "Could not set flags on %s %s!", device_info, device);
187                         return false;
188                 }
189
190                 /* Push arp module to if_fd */
191                 if(ioctl(if_fd, I_PUSH, "arp") < 0) {
192                         logger(LOG_ERR, "Could not push ARP module onto %s %s!", device_info, device);
193                         return false;
194                 }
195
196                 /* Pop any modules on the stream */
197                 while(true) {
198                         if(ioctl(ip_fd, I_POP, NULL) < 0)
199                                 break;
200                 }
201
202                 /* Push arp module to ip_fd */
203                 if(ioctl(ip_fd, I_PUSH, "arp") < 0) {
204                         logger(LOG_ERR, "Could not push ARP module onto %s!", "/dev/ip");
205                         return false;
206                 }
207
208                 /* Open arp_fd */
209                 if((arp_fd = open(device, O_RDWR, 0)) < 0) {
210                         logger(LOG_ERR, "Could not open %s: %s\n", device, strerror(errno));
211                         return false;
212                 }
213
214                 /* Push arp module to arp_fd */
215                 if(ioctl(arp_fd, I_PUSH, "arp") < 0) {
216                         logger(LOG_ERR, "Could not push ARP module onto %s %s!", device_info, device);
217                         return false;
218                 }
219
220                 /* Set ifname to arp */
221                 struct strioctl strioc_if = {
222                         .ic_cmd = SIOCSLIFNAME,
223                         .ic_len = sizeof ifr,
224                         .ic_dp = (char *)&ifr,
225                 };
226
227                 if(ioctl(arp_fd, I_STR, &strioc_if) < 0) {
228                         logger(LOG_ERR, "Could not set ifname to %s %s", device_info, device);
229                         return false;
230                 }
231         }
232
233         int ip_muxid, arp_muxid;
234
235         if((ip_muxid = ioctl(ip_fd, I_PLINK, if_fd)) < 0) {
236                 logger(LOG_ERR, "Could not link %s %s to IP", device_info, device);
237                 return false;
238         }
239
240         if(device_type == DEVICE_TYPE_TAP) {
241                 if((arp_muxid = ioctl(ip_fd, I_PLINK, arp_fd)) < 0) {
242                         logger(LOG_ERR, "Could not link %s %s to ARP", device_info, device);
243                         return false;
244                 }
245                 close(arp_fd);
246         }
247
248         struct lifreq ifr = {};
249         strncpy(ifr.lifr_name, iface, sizeof(ifr.lifr_name));
250         ifr.lifr_ip_muxid = ip_muxid;
251         if(device_type == DEVICE_TYPE_TAP) {
252                 ifr.lifr_arp_muxid = arp_muxid;
253         }
254
255         if(ioctl(ip_fd, SIOCSLIFMUXID, &ifr) < 0) {
256                 if(device_type == DEVICE_TYPE_TAP) {
257                         ioctl(ip_fd, I_PUNLINK, arp_muxid);
258                 }
259                 ioctl(ip_fd, I_PUNLINK, ip_muxid);
260                 logger(LOG_ERR, "Could not set multiplexor id for %s %s", device_info, device);
261                 return false;
262         }
263
264         close(if_fd);
265
266 #ifdef FD_CLOEXEC
267         fcntl(device_fd, F_SETFD, FD_CLOEXEC);
268         fcntl(ip_fd, F_SETFD, FD_CLOEXEC);
269 #endif
270
271         logger(LOG_INFO, "%s is a %s", device, device_info);
272
273         return true;
274 }
275
276 static void close_device(void) {
277         if(iface) {
278                 struct lifreq ifr = {};
279                 strncpy(ifr.lifr_name, iface, sizeof ifr.lifr_name);
280                 if(ioctl(ip_fd, SIOCGLIFMUXID, &ifr) >= 0) {
281                         int muxid = ifr.lifr_arp_muxid;
282                         ioctl(ip_fd, I_PUNLINK, muxid);
283                         muxid = ifr.lifr_ip_muxid;
284                         ioctl(ip_fd, I_PUNLINK, muxid);
285                 }
286         }
287
288         close(ip_fd);
289         close(device_fd);
290
291         free(device);
292         free(iface);
293 }
294
295 static bool read_packet(vpn_packet_t *packet) {
296         int inlen;
297
298         switch(device_type) {
299                 case DEVICE_TYPE_TUN:
300                         if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) {
301                                 logger(LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno));
302                                 return false;
303                         }
304
305                         switch(packet->data[14] >> 4) {
306                                 case 4:
307                                         packet->data[12] = 0x08;
308                                         packet->data[13] = 0x00;
309                                         break;
310                                 case 6:
311                                         packet->data[12] = 0x86;
312                                         packet->data[13] = 0xDD;
313                                         break;
314                                 default:
315                                         logger(DEBUG_TRAFFIC, LOG_ERR, "Unknown IP version %d while reading packet from %s %s", packet->data[14] >> 4, device_info, device);
316                                         return false;
317                         }
318
319                         memset(packet->data, 0, 12);
320                         packet->len = inlen + 14;
321                         break;
322
323                 case DEVICE_TYPE_TAP:
324                         if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
325                                 logger(LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno));
326                                 return false;
327                         }
328
329                         packet->len = inlen + 14;
330                         break;
331
332                 default:
333                         abort();
334         }
335
336         device_total_in += packet->len;
337
338         logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len, device_info);
339
340         return true;
341 }
342
343 static bool write_packet(vpn_packet_t *packet) {
344         logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s", packet->len, device_info);
345
346         switch(device_type) {
347                 case DEVICE_TYPE_TUN:
348                         if(write(device_fd, packet->data + 14, packet->len - 14) < 0) {
349                                 logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno));
350                                 return false;
351                         }
352                         break;
353
354                 case DEVICE_TYPE_TAP:
355                         if(write(device_fd, packet->data, packet->len) < 0) {
356                                 logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno));
357                                 return false;
358                         }
359                         break;
360
361                 default:
362                         abort();
363         }
364
365         device_total_out += packet->len;
366
367         return true;
368 }
369
370 static void dump_device_stats(void) {
371         logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
372         logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
373         logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
374 }
375
376 const devops_t os_devops = {
377         .setup = setup_device,
378         .close = close_device,
379         .read = read_packet,
380         .write = write_packet,
381         .dump_stats = dump_device_stats,
382 };