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
32 #include <sys/ioctl.h>
33 #include <sys/types.h>
34 #include <linux/if_tun.h>
37 #include <dbus/dbus.h>
39 #include <glib/ghash.h>
40 #include <glib/gprintf.h>
42 #include <connman/provider.h>
43 #include <connman/log.h>
44 #include <connman/rtnl.h>
45 #include <connman/task.h>
46 #include <connman/inet.h>
51 struct connman_provider *provider;
56 struct connman_task *task;
59 struct vpn_driver_data {
62 struct vpn_driver *vpn_driver;
63 struct connman_provider_driver provider_driver;
66 GHashTable *driver_hash = NULL;
68 static int kill_tun(char *tun_name)
73 memset(&ifr, 0, sizeof(ifr));
74 ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
75 sprintf(ifr.ifr_name, "%s", tun_name);
77 fd = open("/dev/net/tun", O_RDWR);
80 connman_error("Failed to open /dev/net/tun to device %s: %s",
81 tun_name, strerror(errno));
85 if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
87 connman_error("Failed to TUNSETIFF for device %s to it: %s",
88 tun_name, strerror(errno));
93 if (ioctl(fd, TUNSETPERSIST, 0)) {
95 connman_error("Failed to set tun device %s nonpersistent: %s",
96 tun_name, strerror(errno));
101 DBG("Killed tun device %s", tun_name);
105 void vpn_died(struct connman_task *task, int exit_code, void *user_data)
107 struct connman_provider *provider = user_data;
108 struct vpn_data *data = connman_provider_get_data(provider);
109 int state = data->state;
111 DBG("provider %p data %p", provider, data);
116 kill_tun(data->if_name);
117 connman_provider_set_data(provider, NULL);
118 connman_rtnl_remove_watch(data->watch);
121 if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT)
122 connman_provider_set_state(provider,
123 CONNMAN_PROVIDER_STATE_FAILURE);
125 connman_provider_set_state(provider,
126 CONNMAN_PROVIDER_STATE_IDLE);
128 connman_provider_set_index(provider, -1);
129 connman_provider_unref(data->provider);
132 connman_task_destroy(task);
135 static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
137 struct connman_provider *provider = user_data;
138 struct vpn_data *data = connman_provider_get_data(provider);
140 if ((data->flags & IFF_UP) != (flags & IFF_UP)) {
141 if (flags & IFF_UP) {
142 data->state = VPN_STATE_READY;
143 connman_provider_set_state(provider,
144 CONNMAN_PROVIDER_STATE_READY);
150 static void vpn_notify(struct connman_task *task,
151 DBusMessage *msg, void *user_data)
153 struct connman_provider *provider = user_data;
154 struct vpn_data *data;
155 struct vpn_driver_data *vpn_driver_data;
159 data = connman_provider_get_data(provider);
161 name = connman_provider_get_driver_name(provider);
162 vpn_driver_data = g_hash_table_lookup(driver_hash, name);
163 if (vpn_driver_data == NULL)
166 state = vpn_driver_data->vpn_driver->notify(msg, provider);
168 case VPN_STATE_CONNECT:
169 case VPN_STATE_READY:
170 index = connman_provider_get_index(provider);
171 data->watch = connman_rtnl_add_newlink_watch(index,
172 vpn_newlink, provider);
173 connman_inet_ifup(index);
176 case VPN_STATE_UNKNOWN:
178 case VPN_STATE_DISCONNECT:
179 case VPN_STATE_FAILURE:
180 connman_provider_set_state(provider,
181 CONNMAN_PROVIDER_STATE_DISCONNECT);
186 static int vpn_connect(struct connman_provider *provider)
188 struct vpn_data *data = connman_provider_get_data(provider);
189 struct vpn_driver_data *vpn_driver_data;
198 data = g_try_new0(struct vpn_data, 1);
202 data->provider = connman_provider_ref(provider);
206 data->state = VPN_STATE_IDLE;
208 connman_provider_set_data(provider, data);
210 name = connman_provider_get_driver_name(provider);
211 vpn_driver_data = g_hash_table_lookup(driver_hash, name);
213 fd = open("/dev/net/tun", O_RDWR);
216 connman_error("Failed to open /dev/net/tun: %s",
222 memset(&ifr, 0, sizeof(ifr));
223 ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
225 for (i = 0; i < 256; i++) {
226 sprintf(ifr.ifr_name, "vpn%d", i);
228 if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
233 connman_error("Failed to find available tun device");
239 data->if_name = (char *)g_strdup(ifr.ifr_name);
240 if (!data->if_name) {
245 if (ioctl(fd, TUNSETPERSIST, 1)) {
247 connman_error("Failed to set tun persistent: %s",
256 index = connman_inet_ifindex(data->if_name);
258 connman_error("Failed to get tun ifindex");
259 kill_tun(data->if_name);
263 connman_provider_set_index(provider, index);
265 data->task = connman_task_create(vpn_driver_data->program);
267 if (data->task == NULL) {
269 kill_tun(data->if_name);
273 if (connman_task_set_notify(data->task, "notify",
274 vpn_notify, provider)) {
276 kill_tun(data->if_name);
277 connman_task_destroy(data->task);
282 ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
285 kill_tun(data->if_name);
286 connman_task_destroy(data->task);
291 DBG("%s started with dev %s",
292 vpn_driver_data->provider_driver.name, data->if_name);
294 data->state = VPN_STATE_CONNECT;
299 connman_provider_set_index(provider, -1);
300 connman_provider_set_data(provider, NULL);
301 connman_provider_unref(data->provider);
307 static int vpn_probe(struct connman_provider *provider)
312 static int vpn_disconnect(struct connman_provider *provider)
314 struct vpn_data *data = connman_provider_get_data(provider);
315 struct vpn_driver_data *vpn_driver_data;
318 DBG("disconnect provider %p:", provider);
323 name = connman_provider_get_driver_name(provider);
324 vpn_driver_data = g_hash_table_lookup(driver_hash, name);
325 if (vpn_driver_data->vpn_driver->disconnect)
326 vpn_driver_data->vpn_driver->disconnect();
328 if (data->watch != 0)
329 connman_rtnl_remove_watch(data->watch);
332 data->state = VPN_STATE_DISCONNECT;
333 connman_task_stop(data->task);
338 static int vpn_remove(struct connman_provider *provider)
340 struct vpn_data *data;
342 data = connman_provider_get_data(provider);
343 connman_provider_set_data(provider, NULL);
347 if (data->watch != 0)
348 connman_rtnl_remove_watch(data->watch);
350 connman_task_stop(data->task);
352 g_usleep(G_USEC_PER_SEC);
353 kill_tun(data->if_name);
357 int vpn_register(const char *name, struct vpn_driver *vpn_driver,
360 struct vpn_driver_data *data;
362 data = g_try_new0(struct vpn_driver_data, 1);
367 data->program = program;
369 data->vpn_driver = vpn_driver;
371 data->provider_driver.name = name;
372 data->provider_driver.disconnect = vpn_disconnect;
373 data->provider_driver.connect = vpn_connect;
374 data->provider_driver.probe = vpn_probe;
375 data->provider_driver.remove = vpn_remove;
377 if (driver_hash == NULL) {
378 driver_hash = g_hash_table_new_full(g_str_hash,
383 g_hash_table_insert(driver_hash, (char *)name, data);
385 connman_provider_driver_register(&data->provider_driver);
390 void vpn_unregister(const char *name)
392 struct vpn_driver_data *data;
394 data = g_hash_table_lookup(driver_hash, name);
398 connman_provider_driver_unregister(&data->provider_driver);
400 g_hash_table_remove(driver_hash, name);
402 if (g_hash_table_size(driver_hash) == 0)
403 g_hash_table_destroy(driver_hash);