vpn: Reference of provider not taken
[platform/upstream/connman.git] / plugins / vpn.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
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 version 2 as
9  *  published by the Free Software Foundation.
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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #define _GNU_SOURCE
27 #include <string.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #include <sys/ioctl.h>
34 #include <sys/types.h>
35 #include <linux/if_tun.h>
36 #include <net/if.h>
37
38 #include <dbus/dbus.h>
39
40 #include <glib/ghash.h>
41 #include <glib/gprintf.h>
42
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>
48
49 #include "vpn.h"
50
51 struct vpn_data {
52         struct connman_provider *provider;
53         char *if_name;
54         unsigned flags;
55         unsigned int watch;
56         unsigned int state;
57         struct connman_task *task;
58 };
59
60 struct vpn_driver_data {
61         const char *name;
62         const char *program;
63         struct vpn_driver *vpn_driver;
64         struct connman_provider_driver provider_driver;
65 };
66
67 GHashTable *driver_hash = NULL;
68
69 static int stop_vpn(struct connman_provider *provider)
70 {
71         struct vpn_data *data = connman_provider_get_data(provider);
72         struct vpn_driver_data *vpn_driver_data;
73         const char *name;
74         struct ifreq ifr;
75         int fd, err;
76
77         if (data == NULL)
78                 return -EINVAL;
79
80         name = connman_provider_get_driver_name(provider);
81         if (name == NULL)
82                 return -EINVAL;
83
84         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
85
86         if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
87                         vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN)
88                 return 0;
89
90         memset(&ifr, 0, sizeof(ifr));
91         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
92         sprintf(ifr.ifr_name, "%s", data->if_name);
93
94         fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
95         if (fd < 0) {
96                 err = -errno;
97                 connman_error("Failed to open /dev/net/tun to device %s: %s",
98                               data->if_name, strerror(errno));
99                 return err;
100         }
101
102         if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
103                 err = -errno;
104                 connman_error("Failed to TUNSETIFF for device %s to it: %s",
105                               data->if_name, strerror(errno));
106                 close(fd);
107                 return err;
108         }
109
110         if (ioctl(fd, TUNSETPERSIST, 0)) {
111                 err = -errno;
112                 connman_error("Failed to set tun device %s nonpersistent: %s",
113                               data->if_name, strerror(errno));
114                 close(fd);
115                 return err;
116         }
117         close(fd);
118         DBG("Killed tun device %s", data->if_name);
119         return 0;
120 }
121
122 void vpn_died(struct connman_task *task, int exit_code, void *user_data)
123 {
124         struct connman_provider *provider = user_data;
125         struct vpn_data *data = connman_provider_get_data(provider);
126         int state = VPN_STATE_FAILURE;
127         enum connman_provider_error ret;
128
129         DBG("provider %p data %p", provider, data);
130
131         if (data == NULL)
132                 goto vpn_exit;
133
134         state = data->state;
135
136         stop_vpn(provider);
137         connman_provider_set_data(provider, NULL);
138         connman_provider_unref(provider);
139         connman_rtnl_remove_watch(data->watch);
140
141 vpn_exit:
142         if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) {
143                 const char *name;
144                 struct vpn_driver_data *vpn_data = NULL;
145
146                 name = connman_provider_get_driver_name(provider);
147                 if (name != NULL)
148                         vpn_data = g_hash_table_lookup(driver_hash, name);
149
150                 if (vpn_data != NULL &&
151                                 vpn_data->vpn_driver->error_code != NULL)
152                         ret = vpn_data->vpn_driver->error_code(exit_code);
153                 else
154                         ret = CONNMAN_PROVIDER_ERROR_UNKNOWN;
155
156                 connman_provider_indicate_error(provider, ret);
157         } else
158                 connman_provider_set_state(provider,
159                                                 CONNMAN_PROVIDER_STATE_IDLE);
160
161         connman_provider_set_index(provider, -1);
162         connman_provider_unref(data->provider);
163
164         g_free(data->if_name);
165         g_free(data);
166
167         connman_task_destroy(task);
168 }
169
170 int vpn_set_ifname(struct connman_provider *provider, const char *ifname)
171 {
172         struct vpn_data *data = connman_provider_get_data(provider);
173         int index;
174
175         if (ifname == NULL || data == NULL)
176                 return  -EIO;
177
178         index = connman_inet_ifindex(ifname);
179         if (index < 0)
180                 return  -EIO;
181
182         if (data->if_name != NULL)
183                 g_free(data->if_name);
184
185         data->if_name = (char *)g_strdup(ifname);
186         connman_provider_set_index(provider, index);
187
188         return 0;
189 }
190
191 static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
192 {
193         struct connman_provider *provider = user_data;
194         struct vpn_data *data = connman_provider_get_data(provider);
195
196         if ((data->flags & IFF_UP) != (flags & IFF_UP)) {
197                 if (flags & IFF_UP) {
198                         data->state = VPN_STATE_READY;
199                         connman_provider_set_state(provider,
200                                         CONNMAN_PROVIDER_STATE_READY);
201                 }
202         }
203         data->flags = flags;
204 }
205
206 static DBusMessage *vpn_notify(struct connman_task *task,
207                         DBusMessage *msg, void *user_data)
208 {
209         struct connman_provider *provider = user_data;
210         struct vpn_data *data;
211         struct vpn_driver_data *vpn_driver_data;
212         const char *name;
213         int state, index;
214
215         data = connman_provider_get_data(provider);
216
217         name = connman_provider_get_driver_name(provider);
218         if (name == NULL)
219                 return NULL;
220
221         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
222         if (vpn_driver_data == NULL)
223                 return NULL;
224
225         state = vpn_driver_data->vpn_driver->notify(msg, provider);
226         switch (state) {
227         case VPN_STATE_CONNECT:
228         case VPN_STATE_READY:
229                 index = connman_provider_get_index(provider);
230                 connman_provider_ref(provider);
231                 data->watch = connman_rtnl_add_newlink_watch(index,
232                                                      vpn_newlink, provider);
233                 connman_inet_ifup(index);
234                 break;
235
236         case VPN_STATE_UNKNOWN:
237         case VPN_STATE_IDLE:
238         case VPN_STATE_DISCONNECT:
239         case VPN_STATE_FAILURE:
240                 connman_provider_set_state(provider,
241                                         CONNMAN_PROVIDER_STATE_DISCONNECT);
242                 break;
243
244         case VPN_STATE_AUTH_FAILURE:
245                 connman_provider_indicate_error(provider,
246                                         CONNMAN_PROVIDER_ERROR_AUTH_FAILED);
247                 break;
248         }
249
250         return NULL;
251 }
252
253 static int vpn_create_tun(struct connman_provider *provider)
254 {
255         struct vpn_data *data = connman_provider_get_data(provider);
256         struct ifreq ifr;
257         int i, fd, index;
258         int ret = 0;
259
260         if (data == NULL)
261                 return -EISCONN;
262
263         fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
264         if (fd < 0) {
265                 i = -errno;
266                 connman_error("Failed to open /dev/net/tun: %s",
267                               strerror(errno));
268                 ret = i;
269                 goto exist_err;
270         }
271
272         memset(&ifr, 0, sizeof(ifr));
273         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
274
275         for (i = 0; i < 256; i++) {
276                 sprintf(ifr.ifr_name, "vpn%d", i);
277
278                 if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
279                         break;
280         }
281
282         if (i == 256) {
283                 connman_error("Failed to find available tun device");
284                 close(fd);
285                 ret = -ENODEV;
286                 goto exist_err;
287         }
288
289         data->if_name = (char *)g_strdup(ifr.ifr_name);
290         if (data->if_name == NULL) {
291                 connman_error("Failed to allocate memory");
292                 close(fd);
293                 ret = -ENOMEM;
294                 goto exist_err;
295         }
296
297         if (ioctl(fd, TUNSETPERSIST, 1)) {
298                 i = -errno;
299                 connman_error("Failed to set tun persistent: %s",
300                               strerror(errno));
301                 close(fd);
302                 ret = i;
303                 goto exist_err;
304         }
305
306         close(fd);
307
308         index = connman_inet_ifindex(data->if_name);
309         if (index < 0) {
310                 connman_error("Failed to get tun ifindex");
311                 stop_vpn(provider);
312                 ret = -EIO;
313                 goto exist_err;
314         }
315         connman_provider_set_index(provider, index);
316
317         return 0;
318
319 exist_err:
320         return ret;
321 }
322
323 static int vpn_connect(struct connman_provider *provider)
324 {
325         struct vpn_data *data = connman_provider_get_data(provider);
326         struct vpn_driver_data *vpn_driver_data;
327         const char *name;
328         int ret = 0;
329
330         if (data != NULL)
331                 return -EISCONN;
332
333         data = g_try_new0(struct vpn_data, 1);
334         if (data == NULL)
335                 return -ENOMEM;
336
337         data->provider = connman_provider_ref(provider);
338         data->watch = 0;
339         data->flags = 0;
340         data->task = NULL;
341         data->state = VPN_STATE_IDLE;
342
343         connman_provider_set_data(provider, data);
344
345         name = connman_provider_get_driver_name(provider);
346         if (name == NULL)
347                 return -EINVAL;
348
349         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
350
351         if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
352                 vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
353
354                 ret = vpn_create_tun(provider);
355                 if (ret < 0)
356                         goto exist_err;
357         }
358
359         data->task = connman_task_create(vpn_driver_data->program);
360
361         if (data->task == NULL) {
362                 ret = -ENOMEM;
363                 stop_vpn(provider);
364                 goto exist_err;
365         }
366
367         if (connman_task_set_notify(data->task, "notify",
368                                         vpn_notify, provider)) {
369                 ret = -ENOMEM;
370                 stop_vpn(provider);
371                 connman_task_destroy(data->task);
372                 data->task = NULL;
373                 goto exist_err;
374         }
375
376         ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
377                                                         data->if_name);
378         if (ret < 0) {
379                 stop_vpn(provider);
380                 connman_task_destroy(data->task);
381                 data->task = NULL;
382                 goto exist_err;
383         }
384
385         DBG("%s started with dev %s",
386                 vpn_driver_data->provider_driver.name, data->if_name);
387
388         data->state = VPN_STATE_CONNECT;
389
390         return -EINPROGRESS;
391
392 exist_err:
393         connman_provider_set_index(provider, -1);
394         connman_provider_set_data(provider, NULL);
395         connman_provider_unref(data->provider);
396         g_free(data->if_name);
397         g_free(data);
398
399         return ret;
400 }
401
402 static int vpn_probe(struct connman_provider *provider)
403 {
404         return 0;
405 }
406
407 static int vpn_disconnect(struct connman_provider *provider)
408 {
409         struct vpn_data *data = connman_provider_get_data(provider);
410         struct vpn_driver_data *vpn_driver_data;
411         const char *name;
412
413         DBG("disconnect provider %p:", provider);
414
415         if (data == NULL)
416                 return 0;
417
418         name = connman_provider_get_driver_name(provider);
419         if (name == NULL)
420                 return 0;
421
422         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
423         if (vpn_driver_data->vpn_driver->disconnect)
424                 vpn_driver_data->vpn_driver->disconnect();
425
426         if (data->watch != 0) {
427                 connman_provider_unref(provider);
428                 connman_rtnl_remove_watch(data->watch);
429                 data->watch = 0;
430         }
431
432         data->state = VPN_STATE_DISCONNECT;
433         connman_task_stop(data->task);
434
435         return 0;
436 }
437
438 static int vpn_remove(struct connman_provider *provider)
439 {
440         struct vpn_data *data;
441
442         data = connman_provider_get_data(provider);
443         if (data == NULL)
444                 return 0;
445
446         if (data->watch != 0) {
447                 connman_provider_unref(provider);
448                 connman_rtnl_remove_watch(data->watch);
449                 data->watch = 0;
450         }
451
452         connman_task_stop(data->task);
453
454         g_usleep(G_USEC_PER_SEC);
455         stop_vpn(provider);
456         return 0;
457 }
458
459 static int vpn_save (struct connman_provider *provider, GKeyFile *keyfile)
460 {
461         struct vpn_driver_data *vpn_driver_data;
462         const char *name;
463
464         name = connman_provider_get_driver_name(provider);
465         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
466         if (vpn_driver_data != NULL &&
467                         vpn_driver_data->vpn_driver->save != NULL)
468                 return vpn_driver_data->vpn_driver->save(provider, keyfile);
469
470         return 0;
471 }
472
473 int vpn_register(const char *name, struct vpn_driver *vpn_driver,
474                         const char *program)
475 {
476         struct vpn_driver_data *data;
477
478         data = g_try_new0(struct vpn_driver_data, 1);
479         if (data == NULL)
480                 return -ENOMEM;
481
482         data->name = name;
483         data->program = program;
484
485         data->vpn_driver = vpn_driver;
486
487         data->provider_driver.name = name;
488         data->provider_driver.disconnect = vpn_disconnect;
489         data->provider_driver.connect = vpn_connect;
490         data->provider_driver.probe = vpn_probe;
491         data->provider_driver.remove = vpn_remove;
492         data->provider_driver.save = vpn_save;
493
494         if (driver_hash == NULL)
495                 driver_hash = g_hash_table_new_full(g_str_hash,
496                                                         g_str_equal,
497                                                         NULL, g_free);
498
499         if (driver_hash == NULL) {
500                 connman_error("driver_hash not initialized for %s", name);
501                 g_free(data);
502                 return -ENOMEM;
503         }
504
505         g_hash_table_insert(driver_hash, (char *)name, data);
506
507         connman_provider_driver_register(&data->provider_driver);
508
509         return 0;
510 }
511
512 void vpn_unregister(const char *name)
513 {
514         struct vpn_driver_data *data;
515
516         data = g_hash_table_lookup(driver_hash, name);
517         if (data == NULL)
518                 return;
519
520         connman_provider_driver_unregister(&data->provider_driver);
521
522         g_hash_table_remove(driver_hash, name);
523
524         if (g_hash_table_size(driver_hash) == 0)
525                 g_hash_table_destroy(driver_hash);
526 }