5 * Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
33 #include <sys/ioctl.h>
34 #include <sys/types.h>
35 #include <linux/if_tun.h>
38 #include <dbus/dbus.h>
40 #include <glib/ghash.h>
41 #include <glib/gprintf.h>
43 #include <connman/provider.h>
44 #include <connman/log.h>
45 #include <connman/rtnl.h>
46 #include <connman/task.h>
47 #include <connman/inet.h>
52 struct connman_provider *provider;
57 struct connman_task *task;
60 struct vpn_driver_data {
63 struct vpn_driver *vpn_driver;
64 struct connman_provider_driver provider_driver;
67 GHashTable *driver_hash = NULL;
69 static int kill_tun(char *tun_name)
74 memset(&ifr, 0, sizeof(ifr));
75 ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
76 sprintf(ifr.ifr_name, "%s", tun_name);
78 fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
81 connman_error("Failed to open /dev/net/tun to device %s: %s",
82 tun_name, strerror(errno));
86 if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
88 connman_error("Failed to TUNSETIFF for device %s to it: %s",
89 tun_name, strerror(errno));
94 if (ioctl(fd, TUNSETPERSIST, 0)) {
96 connman_error("Failed to set tun device %s nonpersistent: %s",
97 tun_name, strerror(errno));
102 DBG("Killed tun device %s", tun_name);
106 void vpn_died(struct connman_task *task, int exit_code, void *user_data)
108 struct connman_provider *provider = user_data;
109 struct vpn_data *data = connman_provider_get_data(provider);
110 int state = VPN_STATE_FAILURE;
111 enum connman_provider_error ret;
113 DBG("provider %p data %p", provider, data);
120 kill_tun(data->if_name);
121 connman_provider_set_data(provider, NULL);
122 connman_rtnl_remove_watch(data->watch);
125 if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) {
127 struct vpn_driver_data *vpn_data;
129 name = connman_provider_get_driver_name(provider);
130 vpn_data = g_hash_table_lookup(driver_hash, name);
131 if (vpn_data != NULL &&
132 vpn_data->vpn_driver->error_code != NULL)
133 ret = vpn_data->vpn_driver->error_code(exit_code);
135 ret = CONNMAN_PROVIDER_ERROR_UNKNOWN;
137 connman_provider_indicate_error(provider, ret);
139 connman_provider_set_state(provider,
140 CONNMAN_PROVIDER_STATE_IDLE);
142 connman_provider_set_index(provider, -1);
143 connman_provider_unref(data->provider);
146 connman_task_destroy(task);
149 static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
151 struct connman_provider *provider = user_data;
152 struct vpn_data *data = connman_provider_get_data(provider);
154 if ((data->flags & IFF_UP) != (flags & IFF_UP)) {
155 if (flags & IFF_UP) {
156 data->state = VPN_STATE_READY;
157 connman_provider_set_state(provider,
158 CONNMAN_PROVIDER_STATE_READY);
164 static void vpn_notify(struct connman_task *task,
165 DBusMessage *msg, void *user_data)
167 struct connman_provider *provider = user_data;
168 struct vpn_data *data;
169 struct vpn_driver_data *vpn_driver_data;
173 data = connman_provider_get_data(provider);
175 name = connman_provider_get_driver_name(provider);
176 vpn_driver_data = g_hash_table_lookup(driver_hash, name);
177 if (vpn_driver_data == NULL)
180 state = vpn_driver_data->vpn_driver->notify(msg, provider);
182 case VPN_STATE_CONNECT:
183 case VPN_STATE_READY:
184 index = connman_provider_get_index(provider);
185 data->watch = connman_rtnl_add_newlink_watch(index,
186 vpn_newlink, provider);
187 connman_inet_ifup(index);
190 case VPN_STATE_UNKNOWN:
192 case VPN_STATE_DISCONNECT:
193 case VPN_STATE_FAILURE:
194 connman_provider_set_state(provider,
195 CONNMAN_PROVIDER_STATE_DISCONNECT);
198 case VPN_STATE_AUTH_FAILURE:
199 connman_provider_indicate_error(provider,
200 CONNMAN_PROVIDER_ERROR_AUTH_FAILED);
205 static int vpn_connect(struct connman_provider *provider)
207 struct vpn_data *data = connman_provider_get_data(provider);
208 struct vpn_driver_data *vpn_driver_data;
217 data = g_try_new0(struct vpn_data, 1);
221 data->provider = connman_provider_ref(provider);
225 data->state = VPN_STATE_IDLE;
227 connman_provider_set_data(provider, data);
229 name = connman_provider_get_driver_name(provider);
230 vpn_driver_data = g_hash_table_lookup(driver_hash, name);
232 fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
235 connman_error("Failed to open /dev/net/tun: %s",
241 memset(&ifr, 0, sizeof(ifr));
242 ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
244 for (i = 0; i < 256; i++) {
245 sprintf(ifr.ifr_name, "vpn%d", i);
247 if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
252 connman_error("Failed to find available tun device");
258 data->if_name = (char *)g_strdup(ifr.ifr_name);
259 if (data->if_name == NULL) {
260 connman_error("Failed to allocate memory");
266 if (ioctl(fd, TUNSETPERSIST, 1)) {
268 connman_error("Failed to set tun persistent: %s",
277 index = connman_inet_ifindex(data->if_name);
279 connman_error("Failed to get tun ifindex");
280 kill_tun(data->if_name);
284 connman_provider_set_index(provider, index);
286 data->task = connman_task_create(vpn_driver_data->program);
288 if (data->task == NULL) {
290 kill_tun(data->if_name);
294 if (connman_task_set_notify(data->task, "notify",
295 vpn_notify, provider)) {
297 kill_tun(data->if_name);
298 connman_task_destroy(data->task);
303 ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
306 kill_tun(data->if_name);
307 connman_task_destroy(data->task);
312 DBG("%s started with dev %s",
313 vpn_driver_data->provider_driver.name, data->if_name);
315 data->state = VPN_STATE_CONNECT;
320 connman_provider_set_index(provider, -1);
321 connman_provider_set_data(provider, NULL);
322 connman_provider_unref(data->provider);
328 static int vpn_probe(struct connman_provider *provider)
333 static int vpn_disconnect(struct connman_provider *provider)
335 struct vpn_data *data = connman_provider_get_data(provider);
336 struct vpn_driver_data *vpn_driver_data;
339 DBG("disconnect provider %p:", provider);
344 name = connman_provider_get_driver_name(provider);
345 vpn_driver_data = g_hash_table_lookup(driver_hash, name);
346 if (vpn_driver_data->vpn_driver->disconnect)
347 vpn_driver_data->vpn_driver->disconnect();
349 if (data->watch != 0)
350 connman_rtnl_remove_watch(data->watch);
353 data->state = VPN_STATE_DISCONNECT;
354 connman_task_stop(data->task);
359 static int vpn_remove(struct connman_provider *provider)
361 struct vpn_data *data;
363 data = connman_provider_get_data(provider);
364 connman_provider_set_data(provider, NULL);
368 if (data->watch != 0)
369 connman_rtnl_remove_watch(data->watch);
371 connman_task_stop(data->task);
373 g_usleep(G_USEC_PER_SEC);
374 kill_tun(data->if_name);
378 static int vpn_save (struct connman_provider *provider, GKeyFile *keyfile)
380 struct vpn_driver_data *vpn_driver_data;
383 name = connman_provider_get_driver_name(provider);
384 vpn_driver_data = g_hash_table_lookup(driver_hash, name);
385 if (vpn_driver_data != NULL &&
386 vpn_driver_data->vpn_driver->save != NULL)
387 return vpn_driver_data->vpn_driver->save(provider, keyfile);
392 int vpn_register(const char *name, struct vpn_driver *vpn_driver,
395 struct vpn_driver_data *data;
397 data = g_try_new0(struct vpn_driver_data, 1);
402 data->program = program;
404 data->vpn_driver = vpn_driver;
406 data->provider_driver.name = name;
407 data->provider_driver.disconnect = vpn_disconnect;
408 data->provider_driver.connect = vpn_connect;
409 data->provider_driver.probe = vpn_probe;
410 data->provider_driver.remove = vpn_remove;
411 data->provider_driver.save = vpn_save;
413 if (driver_hash == NULL) {
414 driver_hash = g_hash_table_new_full(g_str_hash,
419 g_hash_table_insert(driver_hash, (char *)name, data);
421 connman_provider_driver_register(&data->provider_driver);
426 void vpn_unregister(const char *name)
428 struct vpn_driver_data *data;
430 data = g_hash_table_lookup(driver_hash, name);
434 connman_provider_driver_unregister(&data->provider_driver);
436 g_hash_table_remove(driver_hash, name);
438 if (g_hash_table_size(driver_hash) == 0)
439 g_hash_table_destroy(driver_hash);