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