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