Imported start of brand new codebase for tinc 2.0.
[tinc] / vnd / vnd.c
1 /*
2     vnd.c -- virtual network device management
3
4     Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
5                   2003-2004 Ivo Timmermans <ivo@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
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21     $Id$
22 */
23
24 #include "system.h"
25
26 #include <linux/if_tun.h>
27
28 #include "fd/fd.h"
29 #include "logger/logger.h"
30 #include "support/xalloc.h"
31
32 #include "vnd/vnd.h"
33
34 vnd_t *vnd_new(void) {
35         vnd_t *vnd;
36
37         return clear(new(vnd));
38 }
39
40 void vnd_free(vnd_t *vnd) {
41         replace(vnd->device, NULL);
42         replace(vnd->interface, NULL);
43         replace(vnd->description, NULL);
44         free(vnd);
45 }
46
47 void vnd_set(vnd_t *vnd, char *device, char *interface, vnd_mode_t mode, vnd_handler_t recv) {
48         replace(vnd->device, device);
49         replace(vnd->interface, interface);
50         vnd->mode = mode;
51         vnd->recv = recv;
52 }
53
54 static bool vnd_send(vnd_t *vnd, char *buf, int len) {
55         int result;
56
57         result = write(vnd->fd.fd, buf, len);
58
59         if(result == len || result < 0 && (errno == EINTR || errno == EAGAIN)) {
60                 logger(LOG_INFO, _("vnd: wrote packet of %d bytes to %s"), len, vnd->description);
61                 return true;
62         }
63
64         logger(LOG_INFO, _("vnd: error writing packet of %d bytes to %s: %s"), len, vnd->description, strerror(errno));
65
66         return false;
67 }
68
69 static bool vnd_recv_handler(fd_t *fd) {
70         vnd_t *vnd = fd->data;
71         char buf[vnd->mtu];
72         int len;
73
74         vnd = fd->data;
75
76         len = read(fd->fd, buf, sizeof(buf));
77
78         if(len > 0) {
79                 logger(LOG_INFO, _("vnd: read packet of %d bytes from %s"), len, vnd->description);
80                 return vnd->recv(vnd, buf, len);
81         }
82
83         if(len < 0 && (errno == EINTR || errno == EAGAIN))
84                 return true;
85
86         logger(LOG_ERR, _("vnd: error reading packet from %s: %s"), vnd->description, strerror(errno));
87
88         return false;
89 }
90
91 bool vnd_open(vnd_t *vnd) {
92         struct ifreq ifr = {0};
93         
94         if(!vnd->device)
95                 vnd->device = xstrdup("/dev/net/tun");
96         
97         vnd->fd.fd = open(vnd->device, O_RDWR | O_NONBLOCK);
98
99         if(vnd->fd.fd < 0) {
100                 logger(LOG_ERR, _("vnd: could not open %s: %s"), vnd->device, strerror(errno));
101                 return false;
102         }
103
104         if(vnd->mode == VND_MODE_TUN)
105                 ifr.ifr_flags = IFF_TUN;
106         else
107                 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
108
109         if(vnd->interface)
110                 strncpy(ifr.ifr_name, vnd->interface, IFNAMSIZ);
111
112         if(!ioctl(vnd->fd.fd, TUNSETIFF, &ifr)) {
113                 if(vnd->interface)
114                         free(vnd->interface);
115                 vnd->interface = xstrdup(ifr.ifr_name);
116         } else {
117                 logger(LOG_ERR, _("vnd: %s is not a Linux tun/tap device"), vnd->device);
118                 return false;
119         }
120
121         if(!vnd->mtu)
122                 vnd->mtu = 1514;
123
124         vnd->send = vnd_send;
125         vnd->fd.mode = FD_MODE_READ;
126         vnd->fd.handler = vnd_recv_handler;
127         vnd->fd.data = vnd;
128
129         if(vnd->description)
130                 free(vnd->description);
131
132         asprintf(&vnd->description, "Linux tun/tap device %s (interface %s)", vnd->device, vnd->interface);
133
134         if(!fd_add(&vnd->fd))
135                 return false;
136
137         logger(LOG_INFO, _("vnd: opened %s"), vnd->description);
138
139         return true;
140 }
141
142 bool vnd_close(vnd_t *vnd) {
143         fd_del(&vnd->fd);
144
145         close(vnd->fd.fd);
146
147         logger(LOG_INFO, _("vnd: closed %s"), vnd->description);
148
149         return true;
150 }
151